From a427ec371ae56c35be4723f09d8379e25134dc56 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 19 Jun 2024 09:00:56 -0400 Subject: [PATCH 1/7] [deps] Platform (CL): Update @compodoc/compodoc to v1.1.25 (#8966) * [deps] Platform (CL): Update @compodoc/compodoc to v1.1.25 * Fix compodoc errors --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Vicki League --- angular.json | 10 +- .../reporting/reporting.component.ts | 32 - .../organizations/tools/tools.component.ts | 38 -- apps/web/src/app/tools/tools.component.ts | 39 -- .../providers/manage/manage.component.ts | 28 - package-lock.json | 575 +++++++++--------- package.json | 4 +- 7 files changed, 289 insertions(+), 437 deletions(-) delete mode 100644 apps/web/src/app/admin-console/organizations/reporting/reporting.component.ts delete mode 100644 apps/web/src/app/admin-console/organizations/tools/tools.component.ts delete mode 100644 apps/web/src/app/tools/tools.component.ts delete mode 100644 bitwarden_license/bit-web/src/app/admin-console/providers/manage/manage.component.ts diff --git a/angular.json b/angular.json index cdf213e39d9..1670491b6fd 100644 --- a/angular.json +++ b/angular.json @@ -160,7 +160,15 @@ "configDir": ".storybook", "browserTarget": "components:build", "compodoc": true, - "compodocArgs": ["-e", "json", "-d", "."], + "compodocArgs": [ + "-p", + "./tsconfig.json", + "-e", + "json", + "-d", + ".", + "--disableRoutesGraph" + ], "outputDir": "storybook-static" } } diff --git a/apps/web/src/app/admin-console/organizations/reporting/reporting.component.ts b/apps/web/src/app/admin-console/organizations/reporting/reporting.component.ts deleted file mode 100644 index 13463fd99fb..00000000000 --- a/apps/web/src/app/admin-console/organizations/reporting/reporting.component.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { Component, OnInit } from "@angular/core"; -import { ActivatedRoute } from "@angular/router"; -import { map, Observable, shareReplay, startWith, switchMap } from "rxjs"; - -import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; -import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; - -@Component({ - selector: "app-org-reporting", - templateUrl: "reporting.component.html", -}) -export class ReportingComponent implements OnInit { - organization$: Observable; - showLeftNav$: Observable; - - constructor( - private route: ActivatedRoute, - private organizationService: OrganizationService, - ) {} - - ngOnInit() { - this.organization$ = this.route.params.pipe( - switchMap((params) => this.organizationService.get$(params.organizationId)), - shareReplay({ refCount: true, bufferSize: 1 }), - ); - - this.showLeftNav$ = this.organization$.pipe( - map((o) => o.canAccessEventLogs && o.canAccessReports), - startWith(true), - ); - } -} diff --git a/apps/web/src/app/admin-console/organizations/tools/tools.component.ts b/apps/web/src/app/admin-console/organizations/tools/tools.component.ts deleted file mode 100644 index 2134c0fe897..00000000000 --- a/apps/web/src/app/admin-console/organizations/tools/tools.component.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { Component } from "@angular/core"; -import { ActivatedRoute } from "@angular/router"; - -import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; -import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; -import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; - -@Component({ - selector: "app-org-tools", - templateUrl: "tools.component.html", -}) -// eslint-disable-next-line rxjs-angular/prefer-takeuntil -export class ToolsComponent { - organization: Organization; - accessReports = false; - loading = true; - - constructor( - private route: ActivatedRoute, - private organizationService: OrganizationService, - private messagingService: MessagingService, - ) {} - - ngOnInit() { - // eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe - this.route.parent.params.subscribe(async (params) => { - this.organization = await this.organizationService.get(params.organizationId); - // TODO: Maybe we want to just make sure they are not on a free plan? Just compare useTotp for now - // since all paid plans include useTotp - this.accessReports = this.organization.useTotp; - this.loading = false; - }); - } - - upgradeOrganization() { - this.messagingService.send("upgradeOrganization", { organizationId: this.organization.id }); - } -} diff --git a/apps/web/src/app/tools/tools.component.ts b/apps/web/src/app/tools/tools.component.ts deleted file mode 100644 index 52ef698fd3c..00000000000 --- a/apps/web/src/app/tools/tools.component.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { Component, OnDestroy, OnInit } from "@angular/core"; -import { Subject, takeUntil } from "rxjs"; - -import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; -import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; - -@Component({ - selector: "app-tools", - templateUrl: "tools.component.html", -}) -export class ToolsComponent implements OnInit, OnDestroy { - private componentIsDestroyed$ = new Subject(); - canAccessPremium = false; - - constructor( - private messagingService: MessagingService, - private billingAccountProfileStateService: BillingAccountProfileStateService, - ) {} - - async ngOnInit() { - this.billingAccountProfileStateService.hasPremiumFromAnySource$ - .pipe(takeUntil(this.componentIsDestroyed$)) - .subscribe((canAccessPremium: boolean) => { - this.canAccessPremium = canAccessPremium; - }); - } - - ngOnDestroy() { - this.componentIsDestroyed$.next(true); - this.componentIsDestroyed$.complete(); - } - - premiumRequired() { - if (!this.canAccessPremium) { - this.messagingService.send("premiumRequired"); - return; - } - } -} diff --git a/bitwarden_license/bit-web/src/app/admin-console/providers/manage/manage.component.ts b/bitwarden_license/bit-web/src/app/admin-console/providers/manage/manage.component.ts deleted file mode 100644 index caacc2dd14a..00000000000 --- a/bitwarden_license/bit-web/src/app/admin-console/providers/manage/manage.component.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { Component, OnInit } from "@angular/core"; -import { ActivatedRoute } from "@angular/router"; - -import { ProviderService } from "@bitwarden/common/admin-console/abstractions/provider.service"; -import { Provider } from "@bitwarden/common/admin-console/models/domain/provider"; - -@Component({ - selector: "provider-manage", - templateUrl: "manage.component.html", -}) -// eslint-disable-next-line rxjs-angular/prefer-takeuntil -export class ManageComponent implements OnInit { - provider: Provider; - accessEvents = false; - - constructor( - private route: ActivatedRoute, - private providerService: ProviderService, - ) {} - - ngOnInit() { - // eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe - this.route.parent.params.subscribe(async (params) => { - this.provider = await this.providerService.get(params.providerId); - this.accessEvents = this.provider.useEvents; - }); - } -} diff --git a/package-lock.json b/package-lock.json index a9b9ec054aa..9aa0719c082 100644 --- a/package-lock.json +++ b/package-lock.json @@ -82,7 +82,7 @@ "@angular/elements": "16.2.12", "@babel/core": "^7.24.6", "@babel/preset-env": "^7.24.6", - "@compodoc/compodoc": "1.1.23", + "@compodoc/compodoc": "1.1.25", "@electron/notarize": "2.3.0", "@electron/rebuild": "3.6.0", "@ngtools/webpack": "16.2.11", @@ -2709,23 +2709,6 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-proposal-private-methods": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz", - "integrity": "sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==", - "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-private-methods instead.", - "dev": true, - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, "node_modules/@babel/plugin-proposal-private-property-in-object": { "version": "7.21.0-placeholder-for-preset-env.2", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", @@ -4485,51 +4468,51 @@ } }, "node_modules/@compodoc/compodoc": { - "version": "1.1.23", - "resolved": "https://registry.npmjs.org/@compodoc/compodoc/-/compodoc-1.1.23.tgz", - "integrity": "sha512-5Zfx+CHKTxLD+TxCGt1U8krnEBCWPVxCLt3jCJEN55AzhTluo8xlMenaXlJsuVqL4Lmo/OTTzEXrm9zoQKh/3w==", + "version": "1.1.25", + "resolved": "https://registry.npmjs.org/@compodoc/compodoc/-/compodoc-1.1.25.tgz", + "integrity": "sha512-MsTEv6S0JGkdXc8pFp3yB/r8Lw49YenD0TCXyIVAmQhWNDtGWi4m2TGz02hdiKAlTJ1McQJFuyXWiItTQtje0A==", "dev": true, "hasInstallScript": true, + "license": "MIT", "dependencies": { - "@angular-devkit/schematics": "14.2.12", - "@babel/core": "^7.23.3", - "@babel/plugin-proposal-private-methods": "^7.18.6", - "@babel/preset-env": "^7.23.3", + "@angular-devkit/schematics": "18.0.1", + "@babel/core": "^7.24.6", + "@babel/plugin-transform-private-methods": "^7.24.6", + "@babel/preset-env": "^7.24.6", "@compodoc/live-server": "^1.2.3", "@compodoc/ngd-transformer": "^2.1.3", - "bootstrap.native": "^5.0.10", + "bootstrap.native": "^5.0.12", "chalk": "4.1.2", "cheerio": "^1.0.0-rc.12", - "chokidar": "^3.5.3", + "chokidar": "^3.6.0", "colors": "1.4.0", - "commander": "^11.1.0", - "cosmiconfig": "^8.3.6", + "commander": "^12.1.0", + "cosmiconfig": "^9.0.0", "decache": "^4.6.2", "es6-shim": "^0.35.8", "fancy-log": "^2.0.0", "fast-glob": "^3.3.2", - "fs-extra": "^11.1.1", - "glob": "^10.3.10", + "fs-extra": "^11.2.0", + "glob": "^10.4.1", "handlebars": "^4.7.8", - "html-entities": "^2.4.0", - "i18next": "^23.7.6", + "html-entities": "^2.5.2", + "i18next": "^23.11.5", "json5": "^2.2.3", "lodash": "^4.17.21", - "loglevel": "^1.8.1", + "loglevel": "^1.9.1", "loglevel-plugin-prefix": "^0.8.4", "lunr": "^2.3.9", "marked": "7.0.3", "minimist": "^1.2.8", "opencollective-postinstall": "^2.0.3", "os-name": "4.0.1", - "pdfjs-dist": "2.12.313", - "pdfmake": "^0.2.8", + "pdfmake": "^0.2.10", "prismjs": "^1.29.0", - "semver": "^7.5.4", + "semver": "^7.6.2", "svg-pan-zoom": "^3.6.1", "tablesort": "^5.3.0", - "traverse": "^0.6.7", - "ts-morph": "^20.0.0", + "traverse": "^0.6.9", + "ts-morph": "^22.0.0", "uuid": "^9.0.1", "vis": "^4.21.0-EOL", "zepto": "^1.2.0" @@ -4538,23 +4521,25 @@ "compodoc": "bin/index-cli.js" }, "engines": { - "node": ">= 14.0.0" + "node": ">= 16.0.0" } }, "node_modules/@compodoc/compodoc/node_modules/@angular-devkit/core": { - "version": "14.2.12", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-14.2.12.tgz", - "integrity": "sha512-tg1+deEZdm3fgk2BQ6y7tujciL6qhtN5Ums266lX//kAZeZ4nNNXTBT+oY5xgfjvmLbW+xKg0XZrAS0oIRKY5g==", + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-18.0.1.tgz", + "integrity": "sha512-91eKZoObs+wRgwssw81Y/94Nvixj0WqJkNusBAg+gAfZTCEeJoGGZJkRK8wrONbM79C3Bx8lN/TfSIPRbjnfOQ==", "dev": true, + "license": "MIT", "dependencies": { - "ajv": "8.11.0", - "ajv-formats": "2.1.1", - "jsonc-parser": "3.1.0", - "rxjs": "6.6.7", + "ajv": "8.13.0", + "ajv-formats": "3.0.1", + "jsonc-parser": "3.2.1", + "picomatch": "4.0.2", + "rxjs": "7.8.1", "source-map": "0.7.4" }, "engines": { - "node": "^14.15.0 || >=16.10.0", + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", "yarn": ">= 1.13.0" }, @@ -4568,182 +4553,126 @@ } }, "node_modules/@compodoc/compodoc/node_modules/@angular-devkit/schematics": { - "version": "14.2.12", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-14.2.12.tgz", - "integrity": "sha512-MN5yGR+SSSPPBBVMf4cifDJn9u0IYvxiHst+HWokH2AkBYy+vB1x8jYES2l1wkiISD7nvjTixfqX+Y95oMBoLg==", + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-18.0.1.tgz", + "integrity": "sha512-AKcEGa3fIgyXT6XTQZWEJZzgmcqlB89fcF7JFOuz4rgQfRmnE2xFw37lKE6ZclCOSiEoffAvgrL8acjdPI1ouw==", "dev": true, + "license": "MIT", "dependencies": { - "@angular-devkit/core": "14.2.12", - "jsonc-parser": "3.1.0", - "magic-string": "0.26.2", + "@angular-devkit/core": "18.0.1", + "jsonc-parser": "3.2.1", + "magic-string": "0.30.10", "ora": "5.4.1", - "rxjs": "6.6.7" + "rxjs": "7.8.1" }, "engines": { - "node": "^14.15.0 || >=16.10.0", + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", "yarn": ">= 1.13.0" } }, - "node_modules/@compodoc/compodoc/node_modules/@babel/plugin-transform-async-to-generator": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.24.1.tgz", - "integrity": "sha512-AawPptitRXp1y0n4ilKcGbRYWfbbzFWz2NqNu7dacYDtFtz0CMjG64b3LQsb3KIgnf4/obcUL78hfaOS7iCUfw==", - "dev": true, - "dependencies": { - "@babel/helper-module-imports": "^7.24.1", - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/helper-remap-async-to-generator": "^7.22.20" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@compodoc/compodoc/node_modules/@babel/preset-env": { - "version": "7.24.3", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.24.3.tgz", - "integrity": "sha512-fSk430k5c2ff8536JcPvPWK4tZDwehWLGlBp0wrsBUjZVdeQV6lePbwKWZaZfK2vnh/1kQX1PzAJWsnBmVgGJA==", - "dev": true, - "dependencies": { - "@babel/compat-data": "^7.24.1", - "@babel/helper-compilation-targets": "^7.23.6", - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/helper-validator-option": "^7.23.5", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.24.1", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.24.1", - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.24.1", - "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-class-properties": "^7.12.13", - "@babel/plugin-syntax-class-static-block": "^7.14.5", - "@babel/plugin-syntax-dynamic-import": "^7.8.3", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3", - "@babel/plugin-syntax-import-assertions": "^7.24.1", - "@babel/plugin-syntax-import-attributes": "^7.24.1", - "@babel/plugin-syntax-import-meta": "^7.10.4", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5", - "@babel/plugin-syntax-top-level-await": "^7.14.5", - "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", - "@babel/plugin-transform-arrow-functions": "^7.24.1", - "@babel/plugin-transform-async-generator-functions": "^7.24.3", - "@babel/plugin-transform-async-to-generator": "^7.24.1", - "@babel/plugin-transform-block-scoped-functions": "^7.24.1", - "@babel/plugin-transform-block-scoping": "^7.24.1", - "@babel/plugin-transform-class-properties": "^7.24.1", - "@babel/plugin-transform-class-static-block": "^7.24.1", - "@babel/plugin-transform-classes": "^7.24.1", - "@babel/plugin-transform-computed-properties": "^7.24.1", - "@babel/plugin-transform-destructuring": "^7.24.1", - "@babel/plugin-transform-dotall-regex": "^7.24.1", - "@babel/plugin-transform-duplicate-keys": "^7.24.1", - "@babel/plugin-transform-dynamic-import": "^7.24.1", - "@babel/plugin-transform-exponentiation-operator": "^7.24.1", - "@babel/plugin-transform-export-namespace-from": "^7.24.1", - "@babel/plugin-transform-for-of": "^7.24.1", - "@babel/plugin-transform-function-name": "^7.24.1", - "@babel/plugin-transform-json-strings": "^7.24.1", - "@babel/plugin-transform-literals": "^7.24.1", - "@babel/plugin-transform-logical-assignment-operators": "^7.24.1", - "@babel/plugin-transform-member-expression-literals": "^7.24.1", - "@babel/plugin-transform-modules-amd": "^7.24.1", - "@babel/plugin-transform-modules-commonjs": "^7.24.1", - "@babel/plugin-transform-modules-systemjs": "^7.24.1", - "@babel/plugin-transform-modules-umd": "^7.24.1", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.22.5", - "@babel/plugin-transform-new-target": "^7.24.1", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.24.1", - "@babel/plugin-transform-numeric-separator": "^7.24.1", - "@babel/plugin-transform-object-rest-spread": "^7.24.1", - "@babel/plugin-transform-object-super": "^7.24.1", - "@babel/plugin-transform-optional-catch-binding": "^7.24.1", - "@babel/plugin-transform-optional-chaining": "^7.24.1", - "@babel/plugin-transform-parameters": "^7.24.1", - "@babel/plugin-transform-private-methods": "^7.24.1", - "@babel/plugin-transform-private-property-in-object": "^7.24.1", - "@babel/plugin-transform-property-literals": "^7.24.1", - "@babel/plugin-transform-regenerator": "^7.24.1", - "@babel/plugin-transform-reserved-words": "^7.24.1", - "@babel/plugin-transform-shorthand-properties": "^7.24.1", - "@babel/plugin-transform-spread": "^7.24.1", - "@babel/plugin-transform-sticky-regex": "^7.24.1", - "@babel/plugin-transform-template-literals": "^7.24.1", - "@babel/plugin-transform-typeof-symbol": "^7.24.1", - "@babel/plugin-transform-unicode-escapes": "^7.24.1", - "@babel/plugin-transform-unicode-property-regex": "^7.24.1", - "@babel/plugin-transform-unicode-regex": "^7.24.1", - "@babel/plugin-transform-unicode-sets-regex": "^7.24.1", - "@babel/preset-modules": "0.1.6-no-external-plugins", - "babel-plugin-polyfill-corejs2": "^0.4.10", - "babel-plugin-polyfill-corejs3": "^0.10.4", - "babel-plugin-polyfill-regenerator": "^0.6.1", - "core-js-compat": "^3.31.0", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@compodoc/compodoc/node_modules/@babel/preset-env/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, "node_modules/@compodoc/compodoc/node_modules/ajv": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", - "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.13.0.tgz", + "integrity": "sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA==", "dev": true, + "license": "MIT", "dependencies": { - "fast-deep-equal": "^3.1.1", + "fast-deep-equal": "^3.1.3", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" + "uri-js": "^4.4.1" }, "funding": { "type": "github", "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/@compodoc/compodoc/node_modules/babel-plugin-polyfill-corejs3": { - "version": "0.10.4", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.10.4.tgz", - "integrity": "sha512-25J6I8NGfa5YkCDogHRID3fVCadIR8/pGl1/spvCkzb6lVn6SR3ojpx9nOn9iEBcUsjY24AmdKm5khcfKdylcg==", + "node_modules/@compodoc/compodoc/node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.6.1", - "core-js-compat": "^3.36.1" + "ajv": "^8.0.0" }, "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } } }, - "node_modules/@compodoc/compodoc/node_modules/babel-plugin-polyfill-regenerator": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.1.tgz", - "integrity": "sha512-JfTApdE++cgcTWjsiCQlLyFBMbTUft9ja17saCc93lgV33h4tuCVj7tlvu//qpLwaG+3yEz7/KhahGrUMkVq9g==", + "node_modules/@compodoc/compodoc/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true, + "license": "Python-2.0" + }, + "node_modules/@compodoc/compodoc/node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.6.1" + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/@compodoc/compodoc/node_modules/commander": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@compodoc/compodoc/node_modules/cosmiconfig": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz", + "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.1", + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" }, "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, "node_modules/@compodoc/compodoc/node_modules/fast-glob": { @@ -4751,6 +4680,7 @@ "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", "dev": true, + "license": "MIT", "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", @@ -4762,42 +4692,62 @@ "node": ">=8.6.0" } }, + "node_modules/@compodoc/compodoc/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, "node_modules/@compodoc/compodoc/node_modules/jsonc-parser": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.1.0.tgz", - "integrity": "sha512-DRf0QjnNeCUds3xTjKlQQ3DpJD51GvDjJfnxUVWg6PZTo2otSm+slzNAxU/35hF8/oJIKoG9slq30JYOsF2azg==", - "dev": true + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.1.tgz", + "integrity": "sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==", + "dev": true, + "license": "MIT" }, "node_modules/@compodoc/compodoc/node_modules/magic-string": { - "version": "0.26.2", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.26.2.tgz", - "integrity": "sha512-NzzlXpclt5zAbmo6h6jNc8zl2gNRGHvmsZW4IvZhTC4W7k4OlLP+S5YLussa/r3ixNT66KOQfNORlXHSOy/X4A==", + "version": "0.30.10", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.10.tgz", + "integrity": "sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==", "dev": true, + "license": "MIT", "dependencies": { - "sourcemap-codec": "^1.4.8" - }, + "@jridgewell/sourcemap-codec": "^1.4.15" + } + }, + "node_modules/@compodoc/compodoc/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "license": "MIT", "engines": { "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/@compodoc/compodoc/node_modules/rxjs": { - "version": "6.6.7", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", - "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "node_modules/@compodoc/compodoc/node_modules/semver": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", "dev": true, - "dependencies": { - "tslib": "^1.9.0" + "license": "ISC", + "bin": { + "semver": "bin/semver.js" }, "engines": { - "npm": ">=2.0.0" + "node": ">=10" } }, - "node_modules/@compodoc/compodoc/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, "node_modules/@compodoc/live-server": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/@compodoc/live-server/-/live-server-1.2.3.tgz", @@ -5958,6 +5908,7 @@ "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", "dev": true, + "license": "ISC", "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", @@ -5975,6 +5926,7 @@ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -5987,6 +5939,7 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -5998,13 +5951,15 @@ "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@isaacs/cliui/node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", "dev": true, + "license": "MIT", "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", @@ -6022,6 +5977,7 @@ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dev": true, + "license": "MIT", "dependencies": { "ansi-regex": "^6.0.1" }, @@ -6037,6 +5993,7 @@ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", @@ -7213,6 +7170,7 @@ "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", "dev": true, + "license": "MIT", "optional": true, "engines": { "node": ">=14" @@ -11255,37 +11213,41 @@ } }, "node_modules/@ts-morph/common": { - "version": "0.21.0", - "resolved": "https://registry.npmjs.org/@ts-morph/common/-/common-0.21.0.tgz", - "integrity": "sha512-ES110Mmne5Vi4ypUKrtVQfXFDtCsDXiUiGxF6ILVlE90dDD4fdpC1LSjydl/ml7xJWKSDZwUYD2zkOePMSrPBA==", + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@ts-morph/common/-/common-0.23.0.tgz", + "integrity": "sha512-m7Lllj9n/S6sOkCkRftpM7L24uvmfXQFedlW/4hENcuJH1HHm9u5EgxZb9uVjQSCGrbBWBkOGgcTxNg36r6ywA==", "dev": true, + "license": "MIT", "dependencies": { - "fast-glob": "^3.2.12", - "minimatch": "^7.4.3", - "mkdirp": "^2.1.6", + "fast-glob": "^3.3.2", + "minimatch": "^9.0.3", + "mkdirp": "^3.0.1", "path-browserify": "^1.0.1" } }, - "node_modules/@ts-morph/common/node_modules/minimatch": { - "version": "7.4.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-7.4.6.tgz", - "integrity": "sha512-sBz8G/YjVniEz6lKPNpKxXwazJe4c19fEfV2GDMX6AjFz+MX9uDWIZW8XreVhkFW3fkIdTv/gxWr/Kks5FFAVw==", + "node_modules/@ts-morph/common/node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", "dev": true, + "license": "MIT", "dependencies": { - "brace-expansion": "^2.0.1" + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=8.6.0" } }, "node_modules/@ts-morph/common/node_modules/mkdirp": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-2.1.6.tgz", - "integrity": "sha512-+hEnITedc8LAtIP9u3HJDFIdcLV2vXP33sqLLIzkv1Db1zO/1OxbvYf0Y1OC/S/Qo5dxHXepofhmxL02PsKe+A==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", + "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", "dev": true, + "license": "MIT", "bin": { "mkdirp": "dist/cjs/src/bin.js" }, @@ -15318,10 +15280,11 @@ } }, "node_modules/bootstrap.native": { - "version": "5.0.11", - "resolved": "https://registry.npmjs.org/bootstrap.native/-/bootstrap.native-5.0.11.tgz", - "integrity": "sha512-bk2i4sQcQk2KuCTs1yygTa+JGjZOpKzIZ/It6TZZOO/Q+PmVGuKuIbrznXF64BUFxXaPNy7gO9LnE7vjGdauSQ==", + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/bootstrap.native/-/bootstrap.native-5.0.12.tgz", + "integrity": "sha512-qTiFBK7//IgdF9u67w3W91U8C2Fc3TGQh61xa0pbtHmD1YRncncFNNs+6ewG2tW7fBGGMXg57gj5d9Qamr0S+w==", "dev": true, + "license": "MIT", "dependencies": { "@thednp/event-listener": "^2.0.4", "@thednp/shorty": "^2.0.0" @@ -16491,10 +16454,11 @@ } }, "node_modules/code-block-writer": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/code-block-writer/-/code-block-writer-12.0.0.tgz", - "integrity": "sha512-q4dMFMlXtKR3XNBHyMHt/3pwYNA69EDk00lloMOaaUMKPUXBw6lpXtbu3MMVG6/uOihGnRDOlkyqsONEUj60+w==", - "dev": true + "version": "13.0.1", + "resolved": "https://registry.npmjs.org/code-block-writer/-/code-block-writer-13.0.1.tgz", + "integrity": "sha512-c5or4P6erEA69TxaxTNcHUNcIn+oyxSRTOWV+pSYF+z4epXqNvwvJ70XPGjPNgue83oAFAPBRQYwpAJ/Hpe/Sg==", + "dev": true, + "license": "MIT" }, "node_modules/code-point-at": { "version": "1.1.0", @@ -18477,7 +18441,8 @@ "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/easy-stack": { "version": "1.0.1", @@ -21731,22 +21696,23 @@ "dev": true }, "node_modules/glob": { - "version": "10.3.10", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", - "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.1.tgz", + "integrity": "sha512-2jelhlq3E4ho74ZyVLN03oKdAZVUa6UDZzFLVH1H7dnoax+y9qyaq8zBkfDIggjniU19z0wU18y16jMB2eyVIw==", "dev": true, + "license": "ISC", "dependencies": { "foreground-child": "^3.1.0", - "jackspeak": "^2.3.5", - "minimatch": "^9.0.1", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", - "path-scurry": "^1.10.1" + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=16 || 14 >=14.18" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -23697,9 +23663,9 @@ } }, "node_modules/i18next": { - "version": "23.10.1", - "resolved": "https://registry.npmjs.org/i18next/-/i18next-23.10.1.tgz", - "integrity": "sha512-NDiIzFbcs3O9PXpfhkjyf7WdqFn5Vq6mhzhtkXzj51aOcNuPNcTwuYNuXCpHsanZGHlHKL35G7huoFeVic1hng==", + "version": "23.11.5", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-23.11.5.tgz", + "integrity": "sha512-41pvpVbW9rhZPk5xjCX2TPJi2861LEig/YRhUkY+1FQ2IQPS0bKUDYnEqY8XPPbB48h1uIwLnP9iiEfuSl20CA==", "dev": true, "funding": [ { @@ -23715,6 +23681,7 @@ "url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project" } ], + "license": "MIT", "dependencies": { "@babel/runtime": "^7.23.2" } @@ -25028,10 +24995,11 @@ } }, "node_modules/jackspeak": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", - "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.1.2.tgz", + "integrity": "sha512-kWmLKn2tRtfYMF/BakihVVRzBKOxz4gJMiL2Rj91WnAB5TPZumSH99R/Yf1qE1u4uRimvCSJfm6hnxohXeEXjQ==", "dev": true, + "license": "BlueOak-1.0.0", "dependencies": { "@isaacs/cliui": "^8.0.2" }, @@ -29531,10 +29499,11 @@ } }, "node_modules/minipass": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", - "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", "dev": true, + "license": "ISC", "engines": { "node": ">=16 || 14 >=14.17" } @@ -32112,26 +32081,28 @@ } }, "node_modules/path-scurry": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz", - "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", "dev": true, + "license": "BlueOak-1.0.0", "dependencies": { - "lru-cache": "^9.1.1 || ^10.0.0", + "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=16 || 14 >=14.18" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/path-scurry/node_modules/lru-cache": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", - "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==", + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.2.tgz", + "integrity": "sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==", "dev": true, + "license": "ISC", "engines": { "node": "14 || >=16.14" } @@ -32165,20 +32136,6 @@ "through": "~2.3" } }, - "node_modules/pdfjs-dist": { - "version": "2.12.313", - "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-2.12.313.tgz", - "integrity": "sha512-1x6iXO4Qnv6Eb+YFdN5JdUzt4pAkxSp3aLAYPX93eQCyg/m7QFzXVWJHJVtoW48CI8HCXju4dSkhQZwoheL5mA==", - "dev": true, - "peerDependencies": { - "worker-loader": "^3.0.8" - }, - "peerDependenciesMeta": { - "worker-loader": { - "optional": true - } - } - }, "node_modules/pdfmake": { "version": "0.2.10", "resolved": "https://registry.npmjs.org/pdfmake/-/pdfmake-0.2.10.tgz", @@ -36027,13 +35984,6 @@ "deprecated": "See https://github.com/lydell/source-map-url#deprecated", "dev": true }, - "node_modules/sourcemap-codec": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", - "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", - "deprecated": "Please use @jridgewell/sourcemap-codec instead", - "dev": true - }, "node_modules/space-separated-tokens": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz", @@ -36515,6 +36465,7 @@ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, + "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -36590,6 +36541,7 @@ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, + "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" }, @@ -37668,10 +37620,16 @@ } }, "node_modules/traverse": { - "version": "0.6.8", - "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.8.tgz", - "integrity": "sha512-aXJDbk6SnumuaZSANd21XAo15ucCDE38H4fkqiGsc3MhCK+wOlZvLP9cB/TvpHT0mOyWgC4Z8EwRlzqYSUzdsA==", + "version": "0.6.9", + "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.9.tgz", + "integrity": "sha512-7bBrcF+/LQzSgFmT0X5YclVqQxtv7TDJ1f8Wj7ibBu/U6BMLeOpUxuZjV7rMc44UtKxlnMFigdhFAIszSX1DMg==", "dev": true, + "license": "MIT", + "dependencies": { + "gopd": "^1.0.1", + "typedarray.prototype.slice": "^1.0.3", + "which-typed-array": "^1.1.15" + }, "engines": { "node": ">= 0.4" }, @@ -37812,13 +37770,14 @@ } }, "node_modules/ts-morph": { - "version": "20.0.0", - "resolved": "https://registry.npmjs.org/ts-morph/-/ts-morph-20.0.0.tgz", - "integrity": "sha512-JVmEJy2Wow5n/84I3igthL9sudQ8qzjh/6i4tmYCm6IqYyKFlNbJZi7oBdjyqcWSWYRu3CtL0xbT6fS03ESZIg==", + "version": "22.0.0", + "resolved": "https://registry.npmjs.org/ts-morph/-/ts-morph-22.0.0.tgz", + "integrity": "sha512-M9MqFGZREyeb5fTl6gNHKZLqBQA0TjA1lea+CR48R8EBTDuWrNqW6ccC5QvjNR4s6wDumD3LTCjOFSp9iwlzaw==", "dev": true, + "license": "MIT", "dependencies": { - "@ts-morph/common": "~0.21.0", - "code-block-writer": "^12.0.0" + "@ts-morph/common": "~0.23.0", + "code-block-writer": "^13.0.1" } }, "node_modules/tsconfig-paths": { @@ -38261,6 +38220,27 @@ "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==" }, + "node_modules/typedarray.prototype.slice": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typedarray.prototype.slice/-/typedarray.prototype.slice-1.0.3.tgz", + "integrity": "sha512-8WbVAQAUlENo1q3c3zZYuy5k9VzBQvp8AX9WOtbvyWlLM1v5JaSRmjubLjzHF4JFtptjH/5c/i95yaElvcjC0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.0", + "es-errors": "^1.3.0", + "typed-array-buffer": "^1.0.2", + "typed-array-byte-offset": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/typescript": { "version": "5.1.6", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz", @@ -40142,6 +40122,7 @@ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", diff --git a/package.json b/package.json index d1bf3a3da57..6886b3b6bc7 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "test:watch": "jest --clearCache && jest --watch", "test:watch:all": "jest --watchAll", "test:types": "node ./scripts/test-types.js", - "docs:json": "compodoc -p ./tsconfig.json -e json -d .", + "docs:json": "compodoc -p ./tsconfig.json -e json -d . --disableRoutesGraph", "storybook": "ng run components:storybook", "build-storybook": "ng run components:build-storybook", "build-storybook:ci": "ng run components:build-storybook --webpack-stats-json", @@ -43,7 +43,7 @@ "@angular/elements": "16.2.12", "@babel/core": "^7.24.6", "@babel/preset-env": "^7.24.6", - "@compodoc/compodoc": "1.1.23", + "@compodoc/compodoc": "1.1.25", "@electron/notarize": "2.3.0", "@electron/rebuild": "3.6.0", "@ngtools/webpack": "16.2.11", From dfd4479a9c4d992ff746bd63717d8e40cbe6cd3c Mon Sep 17 00:00:00 2001 From: KiruthigaManivannan <162679756+KiruthigaManivannan@users.noreply.github.com> Date: Wed, 19 Jun 2024 20:48:31 +0530 Subject: [PATCH 2/7] PM-4960 Migrate Verify Recover Delete Component (#9171) * PM-4960 Migrate Verify Recover Delete Component * PM-4960 Minor fix * PM-4960 Addressed review comments --- .../auth/verify-recover-delete.component.html | 46 ++++++------------- .../auth/verify-recover-delete.component.ts | 36 ++++++--------- apps/web/src/app/oss-routing.module.ts | 20 +++++--- 3 files changed, 41 insertions(+), 61 deletions(-) diff --git a/apps/web/src/app/auth/verify-recover-delete.component.html b/apps/web/src/app/auth/verify-recover-delete.component.html index 975858ac5e5..02581b21418 100644 --- a/apps/web/src/app/auth/verify-recover-delete.component.html +++ b/apps/web/src/app/auth/verify-recover-delete.component.html @@ -1,34 +1,16 @@ -
-
-
-

{{ "deleteAccount" | i18n }}

-
-
- {{ "deleteAccountWarning" | i18n }} -

- {{ email }} -

-

{{ "deleteRecoverConfirmDesc" | i18n }}

-
-
- - - {{ "cancel" | i18n }} - -
-
-
-
+ + {{ "deleteAccountWarning" | i18n }} +

+ {{ email }} +

+

{{ "deleteRecoverConfirmDesc" | i18n }}

+
+
+ + + {{ "cancel" | i18n }} +
diff --git a/apps/web/src/app/auth/verify-recover-delete.component.ts b/apps/web/src/app/auth/verify-recover-delete.component.ts index 92f2c50efa8..b3d380fcbda 100644 --- a/apps/web/src/app/auth/verify-recover-delete.component.ts +++ b/apps/web/src/app/auth/verify-recover-delete.component.ts @@ -1,11 +1,11 @@ import { Component, OnInit } from "@angular/core"; +import { FormGroup } from "@angular/forms"; import { ActivatedRoute, Router } from "@angular/router"; import { first } from "rxjs/operators"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { VerifyDeleteRecoverRequest } from "@bitwarden/common/models/request/verify-delete-recover.request"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; -import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; @Component({ @@ -15,10 +15,10 @@ import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/pl // eslint-disable-next-line rxjs-angular/prefer-takeuntil export class VerifyRecoverDeleteComponent implements OnInit { email: string; - formPromise: Promise; private userId: string; private token: string; + protected formGroup = new FormGroup({}); constructor( private router: Router, @@ -26,7 +26,6 @@ export class VerifyRecoverDeleteComponent implements OnInit { private platformUtilsService: PlatformUtilsService, private i18nService: I18nService, private route: ActivatedRoute, - private logService: LogService, ) {} ngOnInit() { @@ -37,28 +36,19 @@ export class VerifyRecoverDeleteComponent implements OnInit { this.token = qParams.token; this.email = qParams.email; } else { - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.router.navigate(["/"]); + await this.router.navigate(["/"]); } }); } - async submit() { - try { - const request = new VerifyDeleteRecoverRequest(this.userId, this.token); - this.formPromise = this.apiService.postAccountRecoverDeleteToken(request); - await this.formPromise; - this.platformUtilsService.showToast( - "success", - this.i18nService.t("accountDeleted"), - this.i18nService.t("accountDeletedDesc"), - ); - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.router.navigate(["/"]); - } catch (e) { - this.logService.error(e); - } - } + submit = async () => { + const request = new VerifyDeleteRecoverRequest(this.userId, this.token); + await this.apiService.postAccountRecoverDeleteToken(request); + this.platformUtilsService.showToast( + "success", + this.i18nService.t("accountDeleted"), + this.i18nService.t("accountDeletedDesc"), + ); + await this.router.navigate(["/"]); + }; } diff --git a/apps/web/src/app/oss-routing.module.ts b/apps/web/src/app/oss-routing.module.ts index 0c038b76a34..7a57f7a1ce6 100644 --- a/apps/web/src/app/oss-routing.module.ts +++ b/apps/web/src/app/oss-routing.module.ts @@ -136,12 +136,6 @@ const routes: Routes = [ data: { titleId: "acceptFamilySponsorship", doNotSaveUrl: false } satisfies DataProperties, }, { path: "recover", pathMatch: "full", redirectTo: "recover-2fa" }, - { - path: "verify-recover-delete", - component: VerifyRecoverDeleteComponent, - canActivate: [UnauthGuard], - data: { titleId: "deleteAccount" } satisfies DataProperties, - }, { path: "verify-recover-delete-org", component: VerifyRecoverDeleteOrgComponent, @@ -330,6 +324,20 @@ const routes: Routes = [ }, ], }, + { + path: "verify-recover-delete", + canActivate: [unauthGuardFn()], + children: [ + { + path: "", + component: VerifyRecoverDeleteComponent, + data: { + pageTitle: "deleteAccount", + titleId: "deleteAccount", + } satisfies DataProperties & AnonLayoutWrapperData, + }, + ], + }, { path: "remove-password", component: RemovePasswordComponent, From 97002c885209931c6a75a8f1f8d9e7f090385059 Mon Sep 17 00:00:00 2001 From: vinith-kovan <156108204+vinith-kovan@users.noreply.github.com> Date: Wed, 19 Jun 2024 21:05:22 +0530 Subject: [PATCH 3/7] [PM 4973] migrate change kdf component (#8485) * chnage kdf component migration * chnage kdf component migration * change kdf component migration * migrating change-kdf component * migrating change-kdf component * migrating change-kdf component --------- Co-authored-by: Todd Martin --- .../change-kdf/change-kdf.component.html | 175 +++++++++--------- .../change-kdf/change-kdf.component.ts | 132 +++++++++++-- apps/web/src/locales/en/messages.json | 21 +++ 3 files changed, 226 insertions(+), 102 deletions(-) diff --git a/apps/web/src/app/auth/settings/security/change-kdf/change-kdf.component.html b/apps/web/src/app/auth/settings/security/change-kdf/change-kdf.component.html index 0627a886b1a..4442310faca 100644 --- a/apps/web/src/app/auth/settings/security/change-kdf/change-kdf.component.html +++ b/apps/web/src/app/auth/settings/security/change-kdf/change-kdf.component.html @@ -1,117 +1,110 @@ -
-

{{ "encKeySettings" | i18n }}

-
+

{{ "encKeySettings" | i18n }}

{{ "kdfSettingsChangeLogoutWarning" | i18n }} -
-
-
-
- - - - - - - - - -
-
-
-
- - +

+ {{ "higherKDFIterations" | i18n }} +

+

+ {{ + "kdfToHighWarningIncreaseInIncrements" + | i18n: (isPBKDF2(kdfConfig) ? ("incrementsOf100,000" | i18n) : ("smallIncrements" | i18n)) + }} +

+ +
+
+ + {{ "kdfAlgorithm" | i18n }} + + + + + + + {{ "kdfMemory" | i18n }} + + +
+
+
+ + + {{ "kdfIterations" | i18n }} + + + + - + {{ "kdfIterationRecommends" | i18n }} + - - - - + + + {{ "kdfIterations" | i18n }} + + + + + + {{ "kdfParallelism" | i18n }} + + +
-
- -

- {{ "kdfIterationsDesc" | i18n: (PBKDF2_ITERATIONS.defaultValue | number) }} -

- - {{ "kdfIterationsWarning" | i18n: (100000 | number) }} - -
- -

{{ "argon2Desc" | i18n }}

- {{ "argon2Warning" | i18n }} -
-
diff --git a/apps/web/src/app/auth/settings/security/change-kdf/change-kdf.component.ts b/apps/web/src/app/auth/settings/security/change-kdf/change-kdf.component.ts index 5c05f1ba2a6..e9b35dd33d5 100644 --- a/apps/web/src/app/auth/settings/security/change-kdf/change-kdf.component.ts +++ b/apps/web/src/app/auth/settings/security/change-kdf/change-kdf.component.ts @@ -1,4 +1,6 @@ import { Component, OnInit } from "@angular/core"; +import { FormBuilder, FormControl, ValidatorFn, Validators } from "@angular/forms"; +import { Subject, takeUntil } from "rxjs"; import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service"; import { @@ -24,8 +26,34 @@ import { ChangeKdfConfirmationComponent } from "./change-kdf-confirmation.compon }) export class ChangeKdfComponent implements OnInit { kdfConfig: KdfConfig = DEFAULT_KDF_CONFIG; - kdfType = KdfType; kdfOptions: any[] = []; + private destroy$ = new Subject(); + + protected formGroup = this.formBuilder.group({ + kdf: new FormControl(KdfType.PBKDF2_SHA256, [Validators.required]), + kdfConfig: this.formBuilder.group({ + iterations: [ + this.kdfConfig.iterations, + [ + Validators.required, + Validators.min(PBKDF2_ITERATIONS.min), + Validators.max(PBKDF2_ITERATIONS.max), + ], + ], + memory: [ + null as number, + [Validators.required, Validators.min(ARGON2_MEMORY.min), Validators.max(ARGON2_MEMORY.max)], + ], + parallelism: [ + null as number, + [ + Validators.required, + Validators.min(ARGON2_PARALLELISM.min), + Validators.max(ARGON2_PARALLELISM.max), + ], + ], + }), + }); // Default values for template protected PBKDF2_ITERATIONS = PBKDF2_ITERATIONS; @@ -36,6 +64,7 @@ export class ChangeKdfComponent implements OnInit { constructor( private dialogService: DialogService, private kdfConfigService: KdfConfigService, + private formBuilder: FormBuilder, ) { this.kdfOptions = [ { name: "PBKDF2 SHA-256", value: KdfType.PBKDF2_SHA256 }, @@ -45,6 +74,86 @@ export class ChangeKdfComponent implements OnInit { async ngOnInit() { this.kdfConfig = await this.kdfConfigService.getKdfConfig(); + this.formGroup.get("kdf").setValue(this.kdfConfig.kdfType, { emitEvent: false }); + this.setFormControlValues(this.kdfConfig); + + this.formGroup + .get("kdf") + .valueChanges.pipe(takeUntil(this.destroy$)) + .subscribe((newValue) => { + this.updateKdfConfig(newValue); + }); + } + private updateKdfConfig(newValue: KdfType) { + let config: KdfConfig; + const validators: { [key: string]: ValidatorFn[] } = { + iterations: [], + memory: [], + parallelism: [], + }; + + switch (newValue) { + case KdfType.PBKDF2_SHA256: + config = new PBKDF2KdfConfig(); + validators.iterations = [ + Validators.required, + Validators.min(PBKDF2_ITERATIONS.min), + Validators.max(PBKDF2_ITERATIONS.max), + ]; + break; + case KdfType.Argon2id: + config = new Argon2KdfConfig(); + validators.iterations = [ + Validators.required, + Validators.min(ARGON2_ITERATIONS.min), + Validators.max(ARGON2_ITERATIONS.max), + ]; + validators.memory = [ + Validators.required, + Validators.min(ARGON2_MEMORY.min), + Validators.max(ARGON2_MEMORY.max), + ]; + validators.parallelism = [ + Validators.required, + Validators.min(ARGON2_PARALLELISM.min), + Validators.max(ARGON2_PARALLELISM.max), + ]; + break; + default: + throw new Error("Unknown KDF type."); + } + + this.kdfConfig = config; + this.setFormValidators(validators); + this.setFormControlValues(this.kdfConfig); + } + + private setFormValidators(validators: { [key: string]: ValidatorFn[] }) { + this.setValidators("kdfConfig.iterations", validators.iterations); + this.setValidators("kdfConfig.memory", validators.memory); + this.setValidators("kdfConfig.parallelism", validators.parallelism); + } + private setValidators(controlName: string, validators: ValidatorFn[]) { + const control = this.formGroup.get(controlName); + if (control) { + control.setValidators(validators); + control.updateValueAndValidity(); + } + } + private setFormControlValues(kdfConfig: KdfConfig) { + this.formGroup.get("kdfConfig").reset(); + if (kdfConfig.kdfType === KdfType.PBKDF2_SHA256) { + this.formGroup.get("kdfConfig.iterations").setValue(kdfConfig.iterations); + } else if (kdfConfig.kdfType === KdfType.Argon2id) { + this.formGroup.get("kdfConfig.iterations").setValue(kdfConfig.iterations); + this.formGroup.get("kdfConfig.memory").setValue(kdfConfig.memory); + this.formGroup.get("kdfConfig.parallelism").setValue(kdfConfig.parallelism); + } + } + + ngOnDestroy() { + this.destroy$.next(); + this.destroy$.complete(); } isPBKDF2(t: KdfConfig): t is PBKDF2KdfConfig { @@ -55,17 +164,18 @@ export class ChangeKdfComponent implements OnInit { return t instanceof Argon2KdfConfig; } - async onChangeKdf(newValue: KdfType) { - if (newValue === KdfType.PBKDF2_SHA256) { - this.kdfConfig = new PBKDF2KdfConfig(); - } else if (newValue === KdfType.Argon2id) { - this.kdfConfig = new Argon2KdfConfig(); - } else { - throw new Error("Unknown KDF type."); - } - } - async openConfirmationModal() { + this.formGroup.markAllAsTouched(); + if (this.formGroup.invalid) { + return; + } + if (this.kdfConfig.kdfType === KdfType.PBKDF2_SHA256) { + this.kdfConfig.iterations = this.formGroup.get("kdfConfig.iterations").value; + } else if (this.kdfConfig.kdfType === KdfType.Argon2id) { + this.kdfConfig.iterations = this.formGroup.get("kdfConfig.iterations").value; + this.kdfConfig.memory = this.formGroup.get("kdfConfig.memory").value; + this.kdfConfig.parallelism = this.formGroup.get("kdfConfig.parallelism").value; + } this.dialogService.open(ChangeKdfConfirmationComponent, { data: { kdfConfig: this.kdfConfig, diff --git a/apps/web/src/locales/en/messages.json b/apps/web/src/locales/en/messages.json index bd8345ce818..18b45f4d458 100644 --- a/apps/web/src/locales/en/messages.json +++ b/apps/web/src/locales/en/messages.json @@ -8405,5 +8405,26 @@ }, "memberAccessReportDesc": { "message": "Ensure members have access to the right credentials and their accounts are secure. Use this report to obtain a CSV of member access and account configurations." + }, + "higherKDFIterations": { + "message": "Higher KDF iterations can help protect your master password from being brute forced by an attacker." + }, + "incrementsOf100,000": { + "message": "increments of 100,000" + }, + "smallIncrements": { + "message": "small increments" + }, + "kdfIterationRecommends": { + "message": "We recommend 600,000 or more" + }, + "kdfToHighWarningIncreaseInIncrements": { + "message": "For older devices, setting your KDF too high may lead to performance issues. Increase the value in $VALUE$ and test your devices.", + "placeholders": { + "value": { + "content": "$1", + "example":"increments of 100,000" + } + } } } From 7e3ba087ecae764994e09cdc2fca0a5309a114e2 Mon Sep 17 00:00:00 2001 From: KiruthigaManivannan <162679756+KiruthigaManivannan@users.noreply.github.com> Date: Wed, 19 Jun 2024 21:12:16 +0530 Subject: [PATCH 4/7] PM-4950 Migrate Hint Component (#9303) * PM-4950 Migrate Hint Component * PM-4950 Updated Anon layout changes * PM-4950 Addressed review comments * PM-4950 - Tweak form control + add comment. * PM-4950 - Address PR feedback * PM-4950 - Move canActivate up a level. * PM-4950 - Consolidate route under AnonLayoutWrapperComponent path from merging in main. --------- Co-authored-by: Jared Snider <116684653+JaredSnider-Bitwarden@users.noreply.github.com> Co-authored-by: Jared Snider Co-authored-by: Todd Martin --- apps/web/src/app/auth/hint.component.html | 63 ++++++++--------------- apps/web/src/app/auth/hint.component.ts | 26 ++++++++++ apps/web/src/app/oss-routing.module.ts | 25 ++++++--- 3 files changed, 66 insertions(+), 48 deletions(-) diff --git a/apps/web/src/app/auth/hint.component.html b/apps/web/src/app/auth/hint.component.html index 786643d9470..9f4d76d8405 100644 --- a/apps/web/src/app/auth/hint.component.html +++ b/apps/web/src/app/auth/hint.component.html @@ -1,44 +1,23 @@ - -
-
-

{{ "passwordHint" | i18n }}

-
-
-
- - - {{ "enterEmailToGetHint" | i18n }} -
-
-
- - - {{ "cancel" | i18n }} - -
-
-
-
+ + + {{ "emailAddress" | i18n }} + + {{ "enterEmailToGetHint" | i18n }} + +
+
+ + + {{ "cancel" | i18n }} +
diff --git a/apps/web/src/app/auth/hint.component.ts b/apps/web/src/app/auth/hint.component.ts index 116b0f3f830..91e9ca5cebb 100644 --- a/apps/web/src/app/auth/hint.component.ts +++ b/apps/web/src/app/auth/hint.component.ts @@ -1,4 +1,5 @@ import { Component } from "@angular/core"; +import { FormBuilder, Validators } from "@angular/forms"; import { Router } from "@angular/router"; import { HintComponent as BaseHintComponent } from "@bitwarden/angular/auth/components/hint.component"; @@ -13,6 +14,14 @@ import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/pl templateUrl: "hint.component.html", }) export class HintComponent extends BaseHintComponent { + formGroup = this.formBuilder.group({ + email: ["", [Validators.email, Validators.required]], + }); + + get emailFormControl() { + return this.formGroup.controls.email; + } + constructor( router: Router, i18nService: I18nService, @@ -20,7 +29,24 @@ export class HintComponent extends BaseHintComponent { platformUtilsService: PlatformUtilsService, logService: LogService, loginEmailService: LoginEmailServiceAbstraction, + private formBuilder: FormBuilder, ) { super(router, i18nService, apiService, platformUtilsService, logService, loginEmailService); } + + ngOnInit(): void { + super.ngOnInit(); + this.emailFormControl.setValue(this.email); + } + + // Wrapper method to call super.submit() since properties (e.g., submit) cannot use super directly + // This is because properties are assigned per type and generally don't have access to the prototype + async superSubmit() { + await super.submit(); + } + + submit = async () => { + this.email = this.emailFormControl.value; + await this.superSubmit(); + }; } diff --git a/apps/web/src/app/oss-routing.module.ts b/apps/web/src/app/oss-routing.module.ts index 7a57f7a1ce6..e7236348a6c 100644 --- a/apps/web/src/app/oss-routing.module.ts +++ b/apps/web/src/app/oss-routing.module.ts @@ -111,12 +111,6 @@ const routes: Routes = [ component: SetPasswordComponent, data: { titleId: "setMasterPassword" } satisfies DataProperties, }, - { - path: "hint", - component: HintComponent, - canActivate: [UnauthGuard], - data: { titleId: "passwordHint" } satisfies DataProperties, - }, { path: "lock", component: LockComponent, @@ -338,6 +332,25 @@ const routes: Routes = [ }, ], }, + { + path: "hint", + canActivate: [unauthGuardFn()], + children: [ + { + path: "", + component: HintComponent, + data: { + pageTitle: "passwordHint", + titleId: "passwordHint", + } satisfies DataProperties & AnonLayoutWrapperData, + }, + { + path: "", + component: EnvironmentSelectorComponent, + outlet: "environment-selector", + }, + ], + }, { path: "remove-password", component: RemovePasswordComponent, From 88cc37e37f84bda55f2df7a8fba64a989938b6ad Mon Sep 17 00:00:00 2001 From: Todd Martin <106564991+trmartin4@users.noreply.github.com> Date: Wed, 19 Jun 2024 11:51:12 -0400 Subject: [PATCH 5/7] Auth/pm 7672/Update token service to return new token from state (#9706) * Changed return structure * Object changes * Added missing assert. * Updated tests to use SetTokensResult * Fixed constructor * PM-7672 - Fix tests + add new setTokens test around refresh token * Removed change to refreshIdentityToken. * Updated return definition. --------- Co-authored-by: Jared Snider --- .../src/auth/abstractions/token.service.ts | 17 +-- .../auth/models/domain/set-tokens-result.ts | 10 ++ .../src/auth/services/token.service.spec.ts | 100 +++++++++++++++--- .../common/src/auth/services/token.service.ts | 87 ++++++++++----- libs/common/src/services/api.service.ts | 21 ++-- 5 files changed, 173 insertions(+), 62 deletions(-) create mode 100644 libs/common/src/auth/models/domain/set-tokens-result.ts diff --git a/libs/common/src/auth/abstractions/token.service.ts b/libs/common/src/auth/abstractions/token.service.ts index a88dfbb278f..c86b5f1ee39 100644 --- a/libs/common/src/auth/abstractions/token.service.ts +++ b/libs/common/src/auth/abstractions/token.service.ts @@ -3,6 +3,7 @@ import { Observable } from "rxjs"; import { VaultTimeoutAction } from "../../enums/vault-timeout-action.enum"; import { UserId } from "../../types/guid"; import { VaultTimeout } from "../../types/vault-timeout.type"; +import { SetTokensResult } from "../models/domain/set-tokens-result"; import { DecodedAccessToken } from "../services/token.service"; export abstract class TokenService { @@ -23,7 +24,7 @@ export abstract class TokenService { * @param refreshToken The optional refresh token to set. Note: this is undefined when using the CLI Login Via API Key flow * @param clientIdClientSecret The API Key Client ID and Client Secret to set. * - * @returns A promise that resolves when the tokens have been set. + * @returns A promise that resolves with the SetTokensResult containing the tokens that were set. */ setTokens: ( accessToken: string, @@ -31,7 +32,7 @@ export abstract class TokenService { vaultTimeout: VaultTimeout, refreshToken?: string, clientIdClientSecret?: [string, string], - ) => Promise; + ) => Promise; /** * Clears the access token, refresh token, API Key Client ID, and API Key Client Secret out of memory, disk, and secure storage if supported. @@ -47,13 +48,13 @@ export abstract class TokenService { * @param accessToken The access token to set. * @param vaultTimeoutAction The action to take when the vault times out. * @param vaultTimeout The timeout for the vault. - * @returns A promise that resolves when the access token has been set. + * @returns A promise that resolves with the access token that has been set. */ setAccessToken: ( accessToken: string, vaultTimeoutAction: VaultTimeoutAction, vaultTimeout: VaultTimeout, - ) => Promise; + ) => Promise; // TODO: revisit having this public clear method approach once the state service is fully deprecated. /** @@ -86,14 +87,14 @@ export abstract class TokenService { * @param clientId The API Key Client ID to set. * @param vaultTimeoutAction The action to take when the vault times out. * @param vaultTimeout The timeout for the vault. - * @returns A promise that resolves when the API Key Client ID has been set. + * @returns A promise that resolves with the API Key Client ID that has been set. */ setClientId: ( clientId: string, vaultTimeoutAction: VaultTimeoutAction, vaultTimeout: VaultTimeout, userId?: UserId, - ) => Promise; + ) => Promise; /** * Gets the API Key Client ID for the active user. @@ -106,14 +107,14 @@ export abstract class TokenService { * @param clientSecret The API Key Client Secret to set. * @param vaultTimeoutAction The action to take when the vault times out. * @param vaultTimeout The timeout for the vault. - * @returns A promise that resolves when the API Key Client Secret has been set. + * @returns A promise that resolves with the client secret that has been set. */ setClientSecret: ( clientSecret: string, vaultTimeoutAction: VaultTimeoutAction, vaultTimeout: VaultTimeout, userId?: UserId, - ) => Promise; + ) => Promise; /** * Gets the API Key Client Secret for the active user. diff --git a/libs/common/src/auth/models/domain/set-tokens-result.ts b/libs/common/src/auth/models/domain/set-tokens-result.ts new file mode 100644 index 00000000000..3d72edd0d35 --- /dev/null +++ b/libs/common/src/auth/models/domain/set-tokens-result.ts @@ -0,0 +1,10 @@ +export class SetTokensResult { + constructor(accessToken: string, refreshToken?: string, clientIdSecretPair?: [string, string]) { + this.accessToken = accessToken; + this.refreshToken = refreshToken; + this.clientIdSecretPair = clientIdSecretPair; + } + accessToken: string; + refreshToken?: string; + clientIdSecretPair?: [string, string]; +} diff --git a/libs/common/src/auth/services/token.service.spec.ts b/libs/common/src/auth/services/token.service.spec.ts index d7a4c527162..4be945de5f8 100644 --- a/libs/common/src/auth/services/token.service.spec.ts +++ b/libs/common/src/auth/services/token.service.spec.ts @@ -15,6 +15,7 @@ import { SymmetricCryptoKey } from "../../platform/models/domain/symmetric-crypt import { CsprngArray } from "../../types/csprng"; import { UserId } from "../../types/guid"; import { VaultTimeout, VaultTimeoutStringType } from "../../types/vault-timeout.type"; +import { SetTokensResult } from "../models/domain/set-tokens-result"; import { ACCOUNT_ACTIVE_ACCOUNT_ID } from "./account.service"; import { @@ -232,7 +233,7 @@ describe("TokenService", () => { describe("Memory storage tests", () => { it("set the access token in memory", async () => { // Act - await tokenService.setAccessToken( + const result = await tokenService.setAccessToken( accessTokenJwt, memoryVaultTimeoutAction, memoryVaultTimeout, @@ -241,13 +242,14 @@ describe("TokenService", () => { expect( singleUserStateProvider.getFake(userIdFromAccessToken, ACCESS_TOKEN_MEMORY).nextMock, ).toHaveBeenCalledWith(accessTokenJwt); + expect(result).toEqual(accessTokenJwt); }); }); describe("Disk storage tests (secure storage not supported on platform)", () => { it("should set the access token in disk", async () => { // Act - await tokenService.setAccessToken( + const result = await tokenService.setAccessToken( accessTokenJwt, diskVaultTimeoutAction, diskVaultTimeout, @@ -256,6 +258,7 @@ describe("TokenService", () => { expect( singleUserStateProvider.getFake(userIdFromAccessToken, ACCESS_TOKEN_DISK).nextMock, ).toHaveBeenCalledWith(accessTokenJwt); + expect(result).toEqual(accessTokenJwt); }); }); @@ -295,7 +298,7 @@ describe("TokenService", () => { secureStorageService.get.mockResolvedValueOnce(null).mockResolvedValue(accessTokenKeyB64); // Act - await tokenService.setAccessToken( + const result = await tokenService.setAccessToken( accessTokenJwt, diskVaultTimeoutAction, diskVaultTimeout, @@ -318,6 +321,9 @@ describe("TokenService", () => { expect( singleUserStateProvider.getFake(userIdFromAccessToken, ACCESS_TOKEN_MEMORY).nextMock, ).toHaveBeenCalledWith(null); + + // assert that the decrypted access token was returned + expect(result).toEqual(accessTokenJwt); }); it("should fallback to disk storage for the access token if the access token cannot be set in secure storage", async () => { @@ -331,7 +337,7 @@ describe("TokenService", () => { secureStorageService.get.mockResolvedValueOnce(null).mockResolvedValue(null); // Act - await tokenService.setAccessToken( + const result = await tokenService.setAccessToken( accessTokenJwt, diskVaultTimeoutAction, diskVaultTimeout, @@ -355,6 +361,9 @@ describe("TokenService", () => { expect( singleUserStateProvider.getFake(userIdFromAccessToken, ACCESS_TOKEN_DISK).nextMock, ).toHaveBeenCalledWith(accessTokenJwt); + + // assert that the decrypted access token was returned + expect(result).toEqual(accessTokenJwt); }); it("should fallback to disk storage for the access token if secure storage errors on trying to get an existing access token key", async () => { @@ -368,7 +377,7 @@ describe("TokenService", () => { secureStorageService.get.mockRejectedValue(new Error(secureStorageError)); // Act - await tokenService.setAccessToken( + const result = await tokenService.setAccessToken( accessTokenJwt, diskVaultTimeoutAction, diskVaultTimeout, @@ -385,6 +394,9 @@ describe("TokenService", () => { expect( singleUserStateProvider.getFake(userIdFromAccessToken, ACCESS_TOKEN_DISK).nextMock, ).toHaveBeenCalledWith(accessTokenJwt); + + // assert that the decrypted access token was returned + expect(result).toEqual(accessTokenJwt); }); }); }); @@ -2376,18 +2388,21 @@ describe("TokenService", () => { const clientId = "clientId"; const clientSecret = "clientSecret"; - (tokenService as any)._setAccessToken = jest.fn(); // any hack allows for mocking private method. - (tokenService as any).setRefreshToken = jest.fn(); - tokenService.setClientId = jest.fn(); - tokenService.setClientSecret = jest.fn(); + (tokenService as any)._setAccessToken = jest.fn().mockReturnValue(accessTokenJwt); + (tokenService as any).setRefreshToken = jest.fn().mockReturnValue(refreshToken); + tokenService.setClientId = jest.fn().mockReturnValue(clientId); + tokenService.setClientSecret = jest.fn().mockReturnValue(clientSecret); // Act // Note: passing a valid access token so that a valid user id can be determined from the access token - await tokenService.setTokens(accessTokenJwt, vaultTimeoutAction, vaultTimeout, refreshToken, [ - clientId, - clientSecret, - ]); + const result = await tokenService.setTokens( + accessTokenJwt, + vaultTimeoutAction, + vaultTimeout, + refreshToken, + [clientId, clientSecret], + ); // Assert expect((tokenService as any)._setAccessToken).toHaveBeenCalledWith( @@ -2417,6 +2432,44 @@ describe("TokenService", () => { vaultTimeout, userIdFromAccessToken, ); + + expect(result).toStrictEqual( + new SetTokensResult(accessTokenJwt, refreshToken, [clientId, clientSecret]), + ); + }); + + it("does not try to set the refresh token when it is not passed in", async () => { + // Arrange + const vaultTimeoutAction = VaultTimeoutAction.Lock; + const vaultTimeout = 30; + + (tokenService as any)._setAccessToken = jest.fn().mockReturnValue(accessTokenJwt); + (tokenService as any).setRefreshToken = jest.fn(); + tokenService.setClientId = jest.fn(); + tokenService.setClientSecret = jest.fn(); + + // Act + const result = await tokenService.setTokens( + accessTokenJwt, + vaultTimeoutAction, + vaultTimeout, + null, + ); + + // Assert + expect((tokenService as any)._setAccessToken).toHaveBeenCalledWith( + accessTokenJwt, + vaultTimeoutAction, + vaultTimeout, + userIdFromAccessToken, + ); + + // any hack allows for testing private methods + expect((tokenService as any).setRefreshToken).not.toHaveBeenCalled(); + expect(tokenService.setClientId).not.toHaveBeenCalled(); + expect(tokenService.setClientSecret).not.toHaveBeenCalled(); + + expect(result).toStrictEqual(new SetTokensResult(accessTokenJwt)); }); it("does not try to set client id and client secret when they are not passed in", async () => { @@ -2425,13 +2478,18 @@ describe("TokenService", () => { const vaultTimeoutAction = VaultTimeoutAction.Lock; const vaultTimeout = 30; - (tokenService as any)._setAccessToken = jest.fn(); - (tokenService as any).setRefreshToken = jest.fn(); + (tokenService as any)._setAccessToken = jest.fn().mockReturnValue(accessTokenJwt); + (tokenService as any).setRefreshToken = jest.fn().mockReturnValue(refreshToken); tokenService.setClientId = jest.fn(); tokenService.setClientSecret = jest.fn(); // Act - await tokenService.setTokens(accessTokenJwt, vaultTimeoutAction, vaultTimeout, refreshToken); + const result = await tokenService.setTokens( + accessTokenJwt, + vaultTimeoutAction, + vaultTimeout, + refreshToken, + ); // Assert expect((tokenService as any)._setAccessToken).toHaveBeenCalledWith( @@ -2451,6 +2509,8 @@ describe("TokenService", () => { expect(tokenService.setClientId).not.toHaveBeenCalled(); expect(tokenService.setClientSecret).not.toHaveBeenCalled(); + + expect(result).toStrictEqual(new SetTokensResult(accessTokenJwt, refreshToken)); }); it("throws an error when the access token is invalid", async () => { @@ -2535,10 +2595,16 @@ describe("TokenService", () => { (tokenService as any).setRefreshToken = jest.fn(); // Act - await tokenService.setTokens(accessTokenJwt, vaultTimeoutAction, vaultTimeout, refreshToken); + const result = await tokenService.setTokens( + accessTokenJwt, + vaultTimeoutAction, + vaultTimeout, + refreshToken, + ); // Assert expect((tokenService as any).setRefreshToken).not.toHaveBeenCalled(); + expect(result).toStrictEqual(new SetTokensResult(accessTokenJwt)); }); }); diff --git a/libs/common/src/auth/services/token.service.ts b/libs/common/src/auth/services/token.service.ts index 38d0a77b52f..ef7f23cb05a 100644 --- a/libs/common/src/auth/services/token.service.ts +++ b/libs/common/src/auth/services/token.service.ts @@ -21,6 +21,7 @@ import { import { UserId } from "../../types/guid"; import { VaultTimeout, VaultTimeoutStringType } from "../../types/vault-timeout.type"; import { TokenService as TokenServiceAbstraction } from "../abstractions/token.service"; +import { SetTokensResult } from "../models/domain/set-tokens-result"; import { ACCOUNT_ACTIVE_ACCOUNT_ID } from "./account.service"; import { @@ -160,7 +161,7 @@ export class TokenService implements TokenServiceAbstraction { vaultTimeout: VaultTimeout, refreshToken?: string, clientIdClientSecret?: [string, string], - ): Promise { + ): Promise { if (!accessToken) { throw new Error("Access token is required."); } @@ -181,16 +182,40 @@ export class TokenService implements TokenServiceAbstraction { throw new Error("User id not found. Cannot set tokens."); } - await this._setAccessToken(accessToken, vaultTimeoutAction, vaultTimeout, userId); + const newAccessToken = await this._setAccessToken( + accessToken, + vaultTimeoutAction, + vaultTimeout, + userId, + ); + + const newTokens = new SetTokensResult(newAccessToken); if (refreshToken) { - await this.setRefreshToken(refreshToken, vaultTimeoutAction, vaultTimeout, userId); + newTokens.refreshToken = await this.setRefreshToken( + refreshToken, + vaultTimeoutAction, + vaultTimeout, + userId, + ); } if (clientIdClientSecret != null) { - await this.setClientId(clientIdClientSecret[0], vaultTimeoutAction, vaultTimeout, userId); - await this.setClientSecret(clientIdClientSecret[1], vaultTimeoutAction, vaultTimeout, userId); + const clientId = await this.setClientId( + clientIdClientSecret[0], + vaultTimeoutAction, + vaultTimeout, + userId, + ); + const clientSecret = await this.setClientSecret( + clientIdClientSecret[1], + vaultTimeoutAction, + vaultTimeout, + userId, + ); + newTokens.clientIdSecretPair = [clientId, clientSecret]; } + return newTokens; } private async getAccessTokenKey(userId: UserId): Promise { @@ -289,7 +314,7 @@ export class TokenService implements TokenServiceAbstraction { vaultTimeoutAction: VaultTimeoutAction, vaultTimeout: VaultTimeout, userId: UserId, - ): Promise { + ): Promise { const storageLocation = await this.determineStorageLocation( vaultTimeoutAction, vaultTimeout, @@ -302,6 +327,8 @@ export class TokenService implements TokenServiceAbstraction { // store the access token directly. Instead, we encrypt with accessTokenKey and store that // in secure storage. + let decryptedAccessToken: string = null; + try { const encryptedAccessToken: EncString = await this.encryptAccessToken( accessToken, @@ -313,6 +340,10 @@ export class TokenService implements TokenServiceAbstraction { .get(userId, ACCESS_TOKEN_DISK) .update((_) => encryptedAccessToken.encryptedString); + // If we've successfully stored the encrypted access token to disk, we can return the decrypted access token + // so that the caller can use it immediately. + decryptedAccessToken = accessToken; + // TODO: PM-6408 // 2024-02-20: Remove access token from memory so that we migrate to encrypt the access token over time. // Remove this call to remove the access token from memory after 3 months. @@ -324,25 +355,23 @@ export class TokenService implements TokenServiceAbstraction { ); // Fall back to disk storage for unecrypted access token - await this.singleUserStateProvider + decryptedAccessToken = await this.singleUserStateProvider .get(userId, ACCESS_TOKEN_DISK) .update((_) => accessToken); } - return; + return decryptedAccessToken; } case TokenStorageLocation.Disk: // Access token stored on disk unencrypted as platform does not support secure storage - await this.singleUserStateProvider + return await this.singleUserStateProvider .get(userId, ACCESS_TOKEN_DISK) .update((_) => accessToken); - return; case TokenStorageLocation.Memory: // Access token stored in memory due to vault timeout settings - await this.singleUserStateProvider + return await this.singleUserStateProvider .get(userId, ACCESS_TOKEN_MEMORY) .update((_) => accessToken); - return; } } @@ -350,7 +379,7 @@ export class TokenService implements TokenServiceAbstraction { accessToken: string, vaultTimeoutAction: VaultTimeoutAction, vaultTimeout: VaultTimeout, - ): Promise { + ): Promise { if (!accessToken) { throw new Error("Access token is required."); } @@ -370,7 +399,7 @@ export class TokenService implements TokenServiceAbstraction { throw new Error("Vault Timeout Action is required."); } - await this._setAccessToken(accessToken, vaultTimeoutAction, vaultTimeout, userId); + return await this._setAccessToken(accessToken, vaultTimeoutAction, vaultTimeout, userId); } async clearAccessToken(userId?: UserId): Promise { @@ -486,7 +515,7 @@ export class TokenService implements TokenServiceAbstraction { vaultTimeoutAction: VaultTimeoutAction, vaultTimeout: VaultTimeout, userId: UserId, - ): Promise { + ): Promise { // If we don't have a user id, we can't save the value if (!userId) { throw new Error("User id not found. Cannot save refresh token."); @@ -509,6 +538,8 @@ export class TokenService implements TokenServiceAbstraction { switch (storageLocation) { case TokenStorageLocation.SecureStorage: { + let decryptedRefreshToken: string = null; + try { await this.saveStringToSecureStorage( userId, @@ -530,6 +561,10 @@ export class TokenService implements TokenServiceAbstraction { throw new Error("Refresh token failed to save to secure storage."); } + // If we've successfully stored the encrypted refresh token, we can return the decrypted refresh token + // so that the caller can use it immediately. + decryptedRefreshToken = refreshToken; + // TODO: PM-6408 // 2024-02-20: Remove refresh token from memory and disk so that we migrate to secure storage over time. // Remove these 2 calls to remove the refresh token from memory and disk after 3 months. @@ -544,24 +579,22 @@ export class TokenService implements TokenServiceAbstraction { ); // Fall back to disk storage for refresh token - await this.singleUserStateProvider + decryptedRefreshToken = await this.singleUserStateProvider .get(userId, REFRESH_TOKEN_DISK) .update((_) => refreshToken); } - return; + return decryptedRefreshToken; } case TokenStorageLocation.Disk: - await this.singleUserStateProvider + return await this.singleUserStateProvider .get(userId, REFRESH_TOKEN_DISK) .update((_) => refreshToken); - return; case TokenStorageLocation.Memory: - await this.singleUserStateProvider + return await this.singleUserStateProvider .get(userId, REFRESH_TOKEN_MEMORY) .update((_) => refreshToken); - return; } } @@ -644,7 +677,7 @@ export class TokenService implements TokenServiceAbstraction { vaultTimeoutAction: VaultTimeoutAction, vaultTimeout: VaultTimeout, userId?: UserId, - ): Promise { + ): Promise { userId ??= await firstValueFrom(this.activeUserIdGlobalState.state$); // If we don't have a user id, we can't save the value @@ -668,11 +701,11 @@ export class TokenService implements TokenServiceAbstraction { ); if (storageLocation === TokenStorageLocation.Disk) { - await this.singleUserStateProvider + return await this.singleUserStateProvider .get(userId, API_KEY_CLIENT_ID_DISK) .update((_) => clientId); } else if (storageLocation === TokenStorageLocation.Memory) { - await this.singleUserStateProvider + return await this.singleUserStateProvider .get(userId, API_KEY_CLIENT_ID_MEMORY) .update((_) => clientId); } @@ -721,7 +754,7 @@ export class TokenService implements TokenServiceAbstraction { vaultTimeoutAction: VaultTimeoutAction, vaultTimeout: VaultTimeout, userId?: UserId, - ): Promise { + ): Promise { userId ??= await firstValueFrom(this.activeUserIdGlobalState.state$); if (!userId) { @@ -744,11 +777,11 @@ export class TokenService implements TokenServiceAbstraction { ); if (storageLocation === TokenStorageLocation.Disk) { - await this.singleUserStateProvider + return await this.singleUserStateProvider .get(userId, API_KEY_CLIENT_SECRET_DISK) .update((_) => clientSecret); } else if (storageLocation === TokenStorageLocation.Memory) { - await this.singleUserStateProvider + return await this.singleUserStateProvider .get(userId, API_KEY_CLIENT_SECRET_MEMORY) .update((_) => clientSecret); } diff --git a/libs/common/src/services/api.service.ts b/libs/common/src/services/api.service.ts index 48ba6433916..ffb228406b2 100644 --- a/libs/common/src/services/api.service.ts +++ b/libs/common/src/services/api.service.ts @@ -249,7 +249,7 @@ export class ApiService implements ApiServiceAbstraction { async refreshIdentityToken(): Promise { try { - await this.doAuthRefresh(); + await this.refreshToken(); } catch (e) { this.logService.error("Error refreshing access token: ", e); throw e; @@ -1566,8 +1566,7 @@ export class ApiService implements ApiServiceAbstraction { async getActiveBearerToken(): Promise { let accessToken = await this.tokenService.getAccessToken(); if (await this.tokenService.tokenNeedsRefresh()) { - await this.doAuthRefresh(); - accessToken = await this.tokenService.getAccessToken(); + accessToken = await this.refreshToken(); } return accessToken; } @@ -1707,16 +1706,16 @@ export class ApiService implements ApiServiceAbstraction { ); } - protected async doAuthRefresh(): Promise { + protected async refreshToken(): Promise { const refreshToken = await this.tokenService.getRefreshToken(); if (refreshToken != null && refreshToken !== "") { - return this.doRefreshToken(); + return this.refreshAccessToken(); } const clientId = await this.tokenService.getClientId(); const clientSecret = await this.tokenService.getClientSecret(); if (!Utils.isNullOrWhitespace(clientId) && !Utils.isNullOrWhitespace(clientSecret)) { - return this.doApiTokenRefresh(); + return this.refreshApiToken(); } this.refreshAccessTokenErrorCallback(); @@ -1724,7 +1723,7 @@ export class ApiService implements ApiServiceAbstraction { throw new Error("Cannot refresh access token, no refresh token or api keys are stored."); } - protected async doRefreshToken(): Promise { + protected async refreshAccessToken(): Promise { const refreshToken = await this.tokenService.getRefreshToken(); if (refreshToken == null || refreshToken === "") { throw new Error(); @@ -1770,19 +1769,20 @@ export class ApiService implements ApiServiceAbstraction { this.vaultTimeoutSettingsService.getVaultTimeoutByUserId$(userId), ); - await this.tokenService.setTokens( + const refreshedTokens = await this.tokenService.setTokens( tokenResponse.accessToken, vaultTimeoutAction as VaultTimeoutAction, vaultTimeout, tokenResponse.refreshToken, ); + return refreshedTokens.accessToken; } else { const error = await this.handleError(response, true, true); return Promise.reject(error); } } - protected async doApiTokenRefresh(): Promise { + protected async refreshApiToken(): Promise { const clientId = await this.tokenService.getClientId(); const clientSecret = await this.tokenService.getClientSecret(); @@ -1810,11 +1810,12 @@ export class ApiService implements ApiServiceAbstraction { this.vaultTimeoutSettingsService.getVaultTimeoutByUserId$(userId), ); - await this.tokenService.setAccessToken( + const refreshedToken = await this.tokenService.setAccessToken( response.accessToken, vaultTimeoutAction as VaultTimeoutAction, vaultTimeout, ); + return refreshedToken; } async send( From 8d0473163337115963b7c387e5074cb999f42565 Mon Sep 17 00:00:00 2001 From: Daniel James Smith <2670567+djsmith85@users.noreply.github.com> Date: Wed, 19 Jun 2024 18:28:47 +0200 Subject: [PATCH 6/7] [PM-6568][PM-8820][Tech-Debt] Migrate all tools owned toasts to use CL ToastService instead of PlatformUtilsService (#9405) * Migrate all tools owned toasts to use CL ToastService instead of PlatformUtilsService * Fix test that was missing a mock * Fix double checking file and file-content selection --------- Co-authored-by: Daniel James Smith --- .../popup/generator/generator.component.ts | 3 + .../password-generator-history.component.ts | 4 +- .../popup/send/send-add-edit.component.ts | 4 +- .../popup/send/send-groupings.component.ts | 4 +- .../tools/popup/send/send-type.component.ts | 4 +- .../src/app/tools/generator.component.spec.ts | 5 ++ .../src/app/tools/generator.component.ts | 3 + .../password-generator-history.component.ts | 4 +- .../src/app/tools/send/add-edit.component.ts | 14 ++-- .../src/app/tools/send/send.component.ts | 4 +- .../org-vault-export.component.ts | 7 +- apps/web/src/app/tools/generator.component.ts | 4 +- .../password-generator-history.component.ts | 4 +- .../src/app/tools/send/access.component.ts | 15 ++-- .../src/app/tools/send/add-edit.component.ts | 4 +- .../tools/send/send-access-file.component.ts | 22 ++++-- .../tools/send/send-access-text.component.ts | 12 ++-- apps/web/src/app/tools/send/send.component.ts | 10 ++- .../tools/vault-export/export.component.ts | 13 ++-- .../components/generator.component.ts | 15 ++-- .../password-generator-history.component.ts | 12 ++-- .../src/tools/send/add-edit.component.ts | 69 ++++++++++--------- libs/angular/src/tools/send/send.component.ts | 25 ++++--- .../src/components/import.component.ts | 40 +++++------ .../src/components/export.component.ts | 24 +++---- 25 files changed, 197 insertions(+), 128 deletions(-) diff --git a/apps/browser/src/tools/popup/generator/generator.component.ts b/apps/browser/src/tools/popup/generator/generator.component.ts index 1afe696576f..cb671f7201a 100644 --- a/apps/browser/src/tools/popup/generator/generator.component.ts +++ b/apps/browser/src/tools/popup/generator/generator.component.ts @@ -13,6 +13,7 @@ import { UsernameGenerationServiceAbstraction } from "@bitwarden/common/tools/ge import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { AddEditCipherInfo } from "@bitwarden/common/vault/types/add-edit-cipher-info"; +import { ToastService } from "@bitwarden/components"; @Component({ selector: "app-generator", @@ -34,6 +35,7 @@ export class GeneratorComponent extends BaseGeneratorComponent { logService: LogService, ngZone: NgZone, private location: Location, + toastService: ToastService, ) { super( passwordGenerationService, @@ -45,6 +47,7 @@ export class GeneratorComponent extends BaseGeneratorComponent { route, ngZone, window, + toastService, ); this.cipherService = cipherService; } diff --git a/apps/browser/src/tools/popup/generator/password-generator-history.component.ts b/apps/browser/src/tools/popup/generator/password-generator-history.component.ts index 8448077083e..e27c42a111b 100644 --- a/apps/browser/src/tools/popup/generator/password-generator-history.component.ts +++ b/apps/browser/src/tools/popup/generator/password-generator-history.component.ts @@ -5,6 +5,7 @@ import { PasswordGeneratorHistoryComponent as BasePasswordGeneratorHistoryCompon import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { PasswordGenerationServiceAbstraction } from "@bitwarden/common/tools/generator/password"; +import { ToastService } from "@bitwarden/components"; @Component({ selector: "app-password-generator-history", @@ -16,8 +17,9 @@ export class PasswordGeneratorHistoryComponent extends BasePasswordGeneratorHist platformUtilsService: PlatformUtilsService, i18nService: I18nService, private location: Location, + toastService: ToastService, ) { - super(passwordGenerationService, platformUtilsService, i18nService, window); + super(passwordGenerationService, platformUtilsService, i18nService, window, toastService); } close() { diff --git a/apps/browser/src/tools/popup/send/send-add-edit.component.ts b/apps/browser/src/tools/popup/send/send-add-edit.component.ts index 5ff3621e352..7f172178163 100644 --- a/apps/browser/src/tools/popup/send/send-add-edit.component.ts +++ b/apps/browser/src/tools/popup/send/send-add-edit.component.ts @@ -15,7 +15,7 @@ import { MessagingService } from "@bitwarden/common/platform/abstractions/messag import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service.abstraction"; import { SendService } from "@bitwarden/common/tools/send/services/send.service.abstraction"; -import { DialogService } from "@bitwarden/components"; +import { DialogService, ToastService } from "@bitwarden/components"; import BrowserPopupUtils from "../../../platform/popup/browser-popup-utils"; import { BrowserStateService } from "../../../platform/services/abstractions/browser-state.service"; @@ -53,6 +53,7 @@ export class SendAddEditComponent extends BaseAddEditComponent { private filePopoutUtilsService: FilePopoutUtilsService, billingAccountProfileStateService: BillingAccountProfileStateService, accountService: AccountService, + toastService: ToastService, ) { super( i18nService, @@ -69,6 +70,7 @@ export class SendAddEditComponent extends BaseAddEditComponent { formBuilder, billingAccountProfileStateService, accountService, + toastService, ); } diff --git a/apps/browser/src/tools/popup/send/send-groupings.component.ts b/apps/browser/src/tools/popup/send/send-groupings.component.ts index 87d03c4b767..ae76c0ef94f 100644 --- a/apps/browser/src/tools/popup/send/send-groupings.component.ts +++ b/apps/browser/src/tools/popup/send/send-groupings.component.ts @@ -14,7 +14,7 @@ import { SendView } from "@bitwarden/common/tools/send/models/view/send.view"; import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service.abstraction"; import { SendService } from "@bitwarden/common/tools/send/services/send.service.abstraction"; import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction"; -import { DialogService } from "@bitwarden/components"; +import { DialogService, ToastService } from "@bitwarden/components"; import { BrowserSendComponentState } from "../../../models/browserSendComponentState"; import BrowserPopupUtils from "../../../platform/popup/browser-popup-utils"; @@ -49,6 +49,7 @@ export class SendGroupingsComponent extends BaseSendComponent { logService: LogService, sendApiService: SendApiService, dialogService: DialogService, + toastService: ToastService, ) { super( sendService, @@ -61,6 +62,7 @@ export class SendGroupingsComponent extends BaseSendComponent { logService, sendApiService, dialogService, + toastService, ); super.onSuccessfulLoad = async () => { this.selectAll(); diff --git a/apps/browser/src/tools/popup/send/send-type.component.ts b/apps/browser/src/tools/popup/send/send-type.component.ts index aca02587de0..122aa7e021d 100644 --- a/apps/browser/src/tools/popup/send/send-type.component.ts +++ b/apps/browser/src/tools/popup/send/send-type.component.ts @@ -15,7 +15,7 @@ import { SendType } from "@bitwarden/common/tools/send/enums/send-type"; import { SendView } from "@bitwarden/common/tools/send/models/view/send.view"; import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service.abstraction"; import { SendService } from "@bitwarden/common/tools/send/services/send.service.abstraction"; -import { DialogService } from "@bitwarden/components"; +import { DialogService, ToastService } from "@bitwarden/components"; import { BrowserComponentState } from "../../../models/browserComponentState"; import BrowserPopupUtils from "../../../platform/popup/browser-popup-utils"; @@ -51,6 +51,7 @@ export class SendTypeComponent extends BaseSendComponent { logService: LogService, sendApiService: SendApiService, dialogService: DialogService, + toastService: ToastService, ) { super( sendService, @@ -63,6 +64,7 @@ export class SendTypeComponent extends BaseSendComponent { logService, sendApiService, dialogService, + toastService, ); super.onSuccessfulLoad = async () => { this.selectType(this.type); diff --git a/apps/desktop/src/app/tools/generator.component.spec.ts b/apps/desktop/src/app/tools/generator.component.spec.ts index dff7da96004..db8160c2025 100644 --- a/apps/desktop/src/app/tools/generator.component.spec.ts +++ b/apps/desktop/src/app/tools/generator.component.spec.ts @@ -11,6 +11,7 @@ import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/pl import { PasswordGenerationServiceAbstraction } from "@bitwarden/common/tools/generator/password"; import { UsernameGenerationServiceAbstraction } from "@bitwarden/common/tools/generator/username"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; +import { ToastService } from "@bitwarden/components"; import { GeneratorComponent } from "./generator.component"; @@ -59,6 +60,10 @@ describe("GeneratorComponent", () => { provide: AccountService, useValue: mock(), }, + { + provide: ToastService, + useValue: mock(), + }, ], schemas: [NO_ERRORS_SCHEMA], }).compileComponents(); diff --git a/apps/desktop/src/app/tools/generator.component.ts b/apps/desktop/src/app/tools/generator.component.ts index a5c3d393877..0f1443314dc 100644 --- a/apps/desktop/src/app/tools/generator.component.ts +++ b/apps/desktop/src/app/tools/generator.component.ts @@ -8,6 +8,7 @@ import { LogService } from "@bitwarden/common/platform/abstractions/log.service" import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { PasswordGenerationServiceAbstraction } from "@bitwarden/common/tools/generator/password"; import { UsernameGenerationServiceAbstraction } from "@bitwarden/common/tools/generator/username"; +import { ToastService } from "@bitwarden/components"; @Component({ selector: "app-generator", @@ -23,6 +24,7 @@ export class GeneratorComponent extends BaseGeneratorComponent { route: ActivatedRoute, ngZone: NgZone, logService: LogService, + toastService: ToastService, ) { super( passwordGenerationService, @@ -34,6 +36,7 @@ export class GeneratorComponent extends BaseGeneratorComponent { route, ngZone, window, + toastService, ); } diff --git a/apps/desktop/src/app/tools/password-generator-history.component.ts b/apps/desktop/src/app/tools/password-generator-history.component.ts index f62662bd4fa..07ebe5fb4bd 100644 --- a/apps/desktop/src/app/tools/password-generator-history.component.ts +++ b/apps/desktop/src/app/tools/password-generator-history.component.ts @@ -4,6 +4,7 @@ import { PasswordGeneratorHistoryComponent as BasePasswordGeneratorHistoryCompon import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { PasswordGenerationServiceAbstraction } from "@bitwarden/common/tools/generator/password"; +import { ToastService } from "@bitwarden/components"; @Component({ selector: "app-password-generator-history", @@ -14,7 +15,8 @@ export class PasswordGeneratorHistoryComponent extends BasePasswordGeneratorHist passwordGenerationService: PasswordGenerationServiceAbstraction, platformUtilsService: PlatformUtilsService, i18nService: I18nService, + toastService: ToastService, ) { - super(passwordGenerationService, platformUtilsService, i18nService, window); + super(passwordGenerationService, platformUtilsService, i18nService, window, toastService); } } diff --git a/apps/desktop/src/app/tools/send/add-edit.component.ts b/apps/desktop/src/app/tools/send/add-edit.component.ts index 804a3904380..6f2287ea108 100644 --- a/apps/desktop/src/app/tools/send/add-edit.component.ts +++ b/apps/desktop/src/app/tools/send/add-edit.component.ts @@ -14,7 +14,7 @@ import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/pl import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service.abstraction"; import { SendService } from "@bitwarden/common/tools/send/services/send.service.abstraction"; -import { DialogService } from "@bitwarden/components"; +import { DialogService, ToastService } from "@bitwarden/components"; @Component({ selector: "app-send-add-edit", @@ -36,6 +36,7 @@ export class AddEditComponent extends BaseAddEditComponent { formBuilder: FormBuilder, billingAccountProfileStateService: BillingAccountProfileStateService, accountService: AccountService, + toastService: ToastService, ) { super( i18nService, @@ -52,6 +53,7 @@ export class AddEditComponent extends BaseAddEditComponent { formBuilder, billingAccountProfileStateService, accountService, + toastService, ); } @@ -70,11 +72,11 @@ export class AddEditComponent extends BaseAddEditComponent { // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. // eslint-disable-next-line @typescript-eslint/no-floating-promises super.copyLinkToClipboard(link); - this.platformUtilsService.showToast( - "success", - null, - this.i18nService.t("valueCopied", this.i18nService.t("sendLink")), - ); + this.toastService.showToast({ + variant: "success", + title: null, + message: this.i18nService.t("valueCopied", this.i18nService.t("sendLink")), + }); } async resetAndLoad() { diff --git a/apps/desktop/src/app/tools/send/send.component.ts b/apps/desktop/src/app/tools/send/send.component.ts index 75410b2922a..5fa42541864 100644 --- a/apps/desktop/src/app/tools/send/send.component.ts +++ b/apps/desktop/src/app/tools/send/send.component.ts @@ -11,7 +11,7 @@ import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/pl import { SendView } from "@bitwarden/common/tools/send/models/view/send.view"; import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service.abstraction"; import { SendService } from "@bitwarden/common/tools/send/services/send.service.abstraction"; -import { DialogService } from "@bitwarden/components"; +import { DialogService, ToastService } from "@bitwarden/components"; import { invokeMenu, RendererMenuItem } from "../../../utils"; import { SearchBarService } from "../../layout/search/search-bar.service"; @@ -49,6 +49,7 @@ export class SendComponent extends BaseSendComponent implements OnInit, OnDestro logService: LogService, sendApiService: SendApiService, dialogService: DialogService, + toastService: ToastService, ) { super( sendService, @@ -61,6 +62,7 @@ export class SendComponent extends BaseSendComponent implements OnInit, OnDestro logService, sendApiService, dialogService, + toastService, ); // eslint-disable-next-line rxjs-angular/prefer-takeuntil this.searchBarService.searchText$.subscribe((searchText) => { diff --git a/apps/web/src/app/admin-console/organizations/tools/vault-export/org-vault-export.component.ts b/apps/web/src/app/admin-console/organizations/tools/vault-export/org-vault-export.component.ts index e3d19f5487c..0caf39ea794 100644 --- a/apps/web/src/app/admin-console/organizations/tools/vault-export/org-vault-export.component.ts +++ b/apps/web/src/app/admin-console/organizations/tools/vault-export/org-vault-export.component.ts @@ -9,8 +9,7 @@ import { EventType } from "@bitwarden/common/enums"; import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; -import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; -import { DialogService } from "@bitwarden/components"; +import { DialogService, ToastService } from "@bitwarden/components"; import { VaultExportServiceAbstraction } from "@bitwarden/vault-export-core"; import { ExportComponent } from "../../../../tools/vault-export/export.component"; @@ -23,7 +22,7 @@ import { ExportComponent } from "../../../../tools/vault-export/export.component export class OrganizationVaultExportComponent extends ExportComponent { constructor( i18nService: I18nService, - platformUtilsService: PlatformUtilsService, + toastService: ToastService, exportService: VaultExportServiceAbstraction, eventCollectionService: EventCollectionService, private route: ActivatedRoute, @@ -36,7 +35,7 @@ export class OrganizationVaultExportComponent extends ExportComponent { ) { super( i18nService, - platformUtilsService, + toastService, exportService, eventCollectionService, policyService, diff --git a/apps/web/src/app/tools/generator.component.ts b/apps/web/src/app/tools/generator.component.ts index fc27e658465..0a39228f3d6 100644 --- a/apps/web/src/app/tools/generator.component.ts +++ b/apps/web/src/app/tools/generator.component.ts @@ -8,7 +8,7 @@ import { LogService } from "@bitwarden/common/platform/abstractions/log.service" import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { PasswordGenerationServiceAbstraction } from "@bitwarden/common/tools/generator/password"; import { UsernameGenerationServiceAbstraction } from "@bitwarden/common/tools/generator/username"; -import { DialogService } from "@bitwarden/components"; +import { DialogService, ToastService } from "@bitwarden/components"; import { PasswordGeneratorHistoryComponent } from "./password-generator-history.component"; @@ -27,6 +27,7 @@ export class GeneratorComponent extends BaseGeneratorComponent { route: ActivatedRoute, ngZone: NgZone, private dialogService: DialogService, + toastService: ToastService, ) { super( passwordGenerationService, @@ -38,6 +39,7 @@ export class GeneratorComponent extends BaseGeneratorComponent { route, ngZone, window, + toastService, ); if (platformUtilsService.isSelfHost()) { // Allow only valid email forwarders for self host diff --git a/apps/web/src/app/tools/password-generator-history.component.ts b/apps/web/src/app/tools/password-generator-history.component.ts index f62662bd4fa..07ebe5fb4bd 100644 --- a/apps/web/src/app/tools/password-generator-history.component.ts +++ b/apps/web/src/app/tools/password-generator-history.component.ts @@ -4,6 +4,7 @@ import { PasswordGeneratorHistoryComponent as BasePasswordGeneratorHistoryCompon import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { PasswordGenerationServiceAbstraction } from "@bitwarden/common/tools/generator/password"; +import { ToastService } from "@bitwarden/components"; @Component({ selector: "app-password-generator-history", @@ -14,7 +15,8 @@ export class PasswordGeneratorHistoryComponent extends BasePasswordGeneratorHist passwordGenerationService: PasswordGenerationServiceAbstraction, platformUtilsService: PlatformUtilsService, i18nService: I18nService, + toastService: ToastService, ) { - super(passwordGenerationService, platformUtilsService, i18nService, window); + super(passwordGenerationService, platformUtilsService, i18nService, window, toastService); } } diff --git a/apps/web/src/app/tools/send/access.component.ts b/apps/web/src/app/tools/send/access.component.ts index 9915bf52043..64e46b3ff53 100644 --- a/apps/web/src/app/tools/send/access.component.ts +++ b/apps/web/src/app/tools/send/access.component.ts @@ -8,7 +8,6 @@ import { ConfigService } from "@bitwarden/common/platform/abstractions/config/co import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service"; import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; -import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; import { SendType } from "@bitwarden/common/tools/send/enums/send-type"; @@ -18,7 +17,7 @@ import { SendAccessResponse } from "@bitwarden/common/tools/send/models/response import { SendAccessView } from "@bitwarden/common/tools/send/models/view/send-access.view"; import { SEND_KDF_ITERATIONS } from "@bitwarden/common/tools/send/send-kdf"; import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service.abstraction"; -import { NoItemsModule } from "@bitwarden/components"; +import { NoItemsModule, ToastService } from "@bitwarden/components"; import { SharedModule } from "../../shared"; @@ -67,7 +66,7 @@ export class AccessComponent implements OnInit { private route: ActivatedRoute, private cryptoService: CryptoService, private sendApiService: SendApiService, - private platformUtilsService: PlatformUtilsService, + private toastService: ToastService, private i18nService: I18nService, private configService: ConfigService, protected formBuilder: FormBuilder, @@ -142,11 +141,11 @@ export class AccessComponent implements OnInit { } else if (e.statusCode === 404) { this.unavailable = true; } else if (e.statusCode === 400) { - this.platformUtilsService.showToast( - "error", - this.i18nService.t("errorOccurred"), - e.message, - ); + this.toastService.showToast({ + variant: "error", + title: this.i18nService.t("errorOccurred"), + message: e.message, + }); } else { this.error = true; } diff --git a/apps/web/src/app/tools/send/add-edit.component.ts b/apps/web/src/app/tools/send/add-edit.component.ts index cca416db9cd..4dc3001831d 100644 --- a/apps/web/src/app/tools/send/add-edit.component.ts +++ b/apps/web/src/app/tools/send/add-edit.component.ts @@ -15,7 +15,7 @@ import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/pl import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service.abstraction"; import { SendService } from "@bitwarden/common/tools/send/services/send.service.abstraction"; -import { DialogService } from "@bitwarden/components"; +import { DialogService, ToastService } from "@bitwarden/components"; @Component({ selector: "app-send-add-edit", @@ -42,6 +42,7 @@ export class AddEditComponent extends BaseAddEditComponent { protected dialogRef: DialogRef, @Inject(DIALOG_DATA) params: { sendId: string }, accountService: AccountService, + toastService: ToastService, ) { super( i18nService, @@ -58,6 +59,7 @@ export class AddEditComponent extends BaseAddEditComponent { formBuilder, billingAccountProfileStateService, accountService, + toastService, ); this.sendId = params.sendId; diff --git a/apps/web/src/app/tools/send/send-access-file.component.ts b/apps/web/src/app/tools/send/send-access-file.component.ts index 9b1dc5fbc05..8bb3558a69d 100644 --- a/apps/web/src/app/tools/send/send-access-file.component.ts +++ b/apps/web/src/app/tools/send/send-access-file.component.ts @@ -3,13 +3,13 @@ import { Component, Input } from "@angular/core"; import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; -import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { EncArrayBuffer } from "@bitwarden/common/platform/models/domain/enc-array-buffer"; import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; import { SendAccessRequest } from "@bitwarden/common/tools/send/models/request/send-access.request"; import { SendAccessView } from "@bitwarden/common/tools/send/models/view/send-access.view"; import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service.abstraction"; +import { ToastService } from "@bitwarden/components"; import { SharedModule } from "../../shared"; @@ -25,7 +25,7 @@ export class SendAccessFileComponent { @Input() accessRequest: SendAccessRequest; constructor( private i18nService: I18nService, - private platformUtilsService: PlatformUtilsService, + private toastService: ToastService, private cryptoService: CryptoService, private fileDownloadService: FileDownloadService, private sendApiService: SendApiService, @@ -42,13 +42,21 @@ export class SendAccessFileComponent { ); if (Utils.isNullOrWhitespace(downloadData.url)) { - this.platformUtilsService.showToast("error", null, this.i18nService.t("missingSendFile")); + this.toastService.showToast({ + variant: "error", + title: null, + message: this.i18nService.t("missingSendFile"), + }); return; } const response = await fetch(new Request(downloadData.url, { cache: "no-store" })); if (response.status !== 200) { - this.platformUtilsService.showToast("error", null, this.i18nService.t("errorOccurred")); + this.toastService.showToast({ + variant: "error", + title: null, + message: this.i18nService.t("errorOccurred"), + }); return; } @@ -61,7 +69,11 @@ export class SendAccessFileComponent { downloadMethod: "save", }); } catch (e) { - this.platformUtilsService.showToast("error", null, this.i18nService.t("errorOccurred")); + this.toastService.showToast({ + variant: "error", + title: null, + message: this.i18nService.t("errorOccurred"), + }); } }; } diff --git a/apps/web/src/app/tools/send/send-access-text.component.ts b/apps/web/src/app/tools/send/send-access-text.component.ts index f86a24f14a4..290bde50cd9 100644 --- a/apps/web/src/app/tools/send/send-access-text.component.ts +++ b/apps/web/src/app/tools/send/send-access-text.component.ts @@ -4,6 +4,7 @@ import { FormBuilder } from "@angular/forms"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { SendAccessView } from "@bitwarden/common/tools/send/models/view/send-access.view"; +import { ToastService } from "@bitwarden/components"; import { SharedModule } from "../../shared"; @@ -25,6 +26,7 @@ export class SendAccessTextComponent { private i18nService: I18nService, private platformUtilsService: PlatformUtilsService, private formBuilder: FormBuilder, + private toastService: ToastService, ) {} get send(): SendAccessView { @@ -46,11 +48,11 @@ export class SendAccessTextComponent { protected copyText() { this.platformUtilsService.copyToClipboard(this.send.text.text); - this.platformUtilsService.showToast( - "success", - null, - this.i18nService.t("valueCopied", this.i18nService.t("sendTypeText")), - ); + this.toastService.showToast({ + variant: "success", + title: null, + message: this.i18nService.t("valueCopied", this.i18nService.t("sendTypeText")), + }); } protected toggleText() { diff --git a/apps/web/src/app/tools/send/send.component.ts b/apps/web/src/app/tools/send/send.component.ts index 755435882ac..56f7734ca14 100644 --- a/apps/web/src/app/tools/send/send.component.ts +++ b/apps/web/src/app/tools/send/send.component.ts @@ -12,7 +12,13 @@ import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/pl import { SendView } from "@bitwarden/common/tools/send/models/view/send.view"; import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service.abstraction"; import { SendService } from "@bitwarden/common/tools/send/services/send.service.abstraction"; -import { DialogService, NoItemsModule, SearchModule, TableDataSource } from "@bitwarden/components"; +import { + DialogService, + NoItemsModule, + SearchModule, + TableDataSource, + ToastService, +} from "@bitwarden/components"; import { HeaderModule } from "../../layouts/header/header.module"; import { SharedModule } from "../../shared"; @@ -56,6 +62,7 @@ export class SendComponent extends BaseSendComponent { logService: LogService, sendApiService: SendApiService, dialogService: DialogService, + toastService: ToastService, ) { super( sendService, @@ -68,6 +75,7 @@ export class SendComponent extends BaseSendComponent { logService, sendApiService, dialogService, + toastService, ); } diff --git a/apps/web/src/app/tools/vault-export/export.component.ts b/apps/web/src/app/tools/vault-export/export.component.ts index df53e599ed7..8b5f82167d6 100644 --- a/apps/web/src/app/tools/vault-export/export.component.ts +++ b/apps/web/src/app/tools/vault-export/export.component.ts @@ -7,8 +7,7 @@ import { PolicyService } from "@bitwarden/common/admin-console/abstractions/poli import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; -import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; -import { DialogService } from "@bitwarden/components"; +import { DialogService, ToastService } from "@bitwarden/components"; import { VaultExportServiceAbstraction } from "@bitwarden/vault-export-core"; import { ExportComponent as BaseExportComponent } from "@bitwarden/vault-export-ui"; @@ -19,7 +18,7 @@ import { ExportComponent as BaseExportComponent } from "@bitwarden/vault-export- export class ExportComponent extends BaseExportComponent { constructor( i18nService: I18nService, - platformUtilsService: PlatformUtilsService, + toastService: ToastService, exportService: VaultExportServiceAbstraction, eventCollectionService: EventCollectionService, policyService: PolicyService, @@ -31,7 +30,7 @@ export class ExportComponent extends BaseExportComponent { ) { super( i18nService, - platformUtilsService, + toastService, exportService, eventCollectionService, policyService, @@ -45,6 +44,10 @@ export class ExportComponent extends BaseExportComponent { protected saved() { super.saved(); - this.platformUtilsService.showToast("success", null, this.i18nService.t("exportSuccess")); + this.toastService.showToast({ + variant: "success", + title: null, + message: this.i18nService.t("exportSuccess"), + }); } } diff --git a/libs/angular/src/tools/generator/components/generator.component.ts b/libs/angular/src/tools/generator/components/generator.component.ts index b94d9bc6f0e..1f7c931df30 100644 --- a/libs/angular/src/tools/generator/components/generator.component.ts +++ b/libs/angular/src/tools/generator/components/generator.component.ts @@ -19,6 +19,7 @@ import { UsernameGeneratorOptions, } from "@bitwarden/common/tools/generator/username"; import { EmailForwarderOptions } from "@bitwarden/common/tools/models/domain/email-forwarder-options"; +import { ToastService } from "@bitwarden/components"; @Directive() export class GeneratorComponent implements OnInit, OnDestroy { @@ -67,6 +68,7 @@ export class GeneratorComponent implements OnInit, OnDestroy { protected route: ActivatedRoute, protected ngZone: NgZone, private win: Window, + protected toastService: ToastService, ) { this.typeOptions = [ { name: i18nService.t("password"), value: "password" }, @@ -317,11 +319,14 @@ export class GeneratorComponent implements OnInit, OnDestroy { password ? this.password : this.username, copyOptions, ); - this.platformUtilsService.showToast( - "info", - null, - this.i18nService.t("valueCopied", this.i18nService.t(password ? "password" : "username")), - ); + this.toastService.showToast({ + variant: "info", + title: null, + message: this.i18nService.t( + "valueCopied", + this.i18nService.t(password ? "password" : "username"), + ), + }); } select() { diff --git a/libs/angular/src/tools/generator/components/password-generator-history.component.ts b/libs/angular/src/tools/generator/components/password-generator-history.component.ts index 9ad0c0cbdbb..f0438a9a5a3 100644 --- a/libs/angular/src/tools/generator/components/password-generator-history.component.ts +++ b/libs/angular/src/tools/generator/components/password-generator-history.component.ts @@ -6,6 +6,7 @@ import { GeneratedPasswordHistory, PasswordGenerationServiceAbstraction, } from "@bitwarden/common/tools/generator/password"; +import { ToastService } from "@bitwarden/components"; @Directive() export class PasswordGeneratorHistoryComponent implements OnInit { @@ -16,6 +17,7 @@ export class PasswordGeneratorHistoryComponent implements OnInit { protected platformUtilsService: PlatformUtilsService, protected i18nService: I18nService, private win: Window, + protected toastService: ToastService, ) {} async ngOnInit() { @@ -29,10 +31,10 @@ export class PasswordGeneratorHistoryComponent implements OnInit { copy(password: string) { const copyOptions = this.win != null ? { window: this.win } : null; this.platformUtilsService.copyToClipboard(password, copyOptions); - this.platformUtilsService.showToast( - "info", - null, - this.i18nService.t("valueCopied", this.i18nService.t("password")), - ); + this.toastService.showToast({ + variant: "info", + title: null, + message: this.i18nService.t("valueCopied", this.i18nService.t("password")), + }); } } diff --git a/libs/angular/src/tools/send/add-edit.component.ts b/libs/angular/src/tools/send/add-edit.component.ts index b4f7ec171a1..a398ed1969d 100644 --- a/libs/angular/src/tools/send/add-edit.component.ts +++ b/libs/angular/src/tools/send/add-edit.component.ts @@ -22,7 +22,7 @@ import { SendTextView } from "@bitwarden/common/tools/send/models/view/send-text import { SendView } from "@bitwarden/common/tools/send/models/view/send.view"; import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service.abstraction"; import { SendService } from "@bitwarden/common/tools/send/services/send.service.abstraction"; -import { DialogService } from "@bitwarden/components"; +import { DialogService, ToastService } from "@bitwarden/components"; // Value = hours enum DatePreset { @@ -120,6 +120,7 @@ export class AddEditComponent implements OnInit, OnDestroy { protected formBuilder: FormBuilder, protected billingAccountProfileStateService: BillingAccountProfileStateService, protected accountService: AccountService, + protected toastService: ToastService, ) { this.typeOptions = [ { name: i18nService.t("sendTypeFile"), value: SendType.File, premium: true }, @@ -269,11 +270,11 @@ export class AddEditComponent implements OnInit, OnDestroy { this.formGroup.markAllAsTouched(); if (this.disableSend) { - this.platformUtilsService.showToast( - "error", - this.i18nService.t("errorOccurred"), - this.i18nService.t("sendDisabledWarning"), - ); + this.toastService.showToast({ + variant: "error", + title: this.i18nService.t("errorOccurred"), + message: this.i18nService.t("sendDisabledWarning"), + }); return false; } @@ -289,11 +290,11 @@ export class AddEditComponent implements OnInit, OnDestroy { this.send.type = this.type; if (Utils.isNullOrWhitespace(this.send.name)) { - this.platformUtilsService.showToast( - "error", - this.i18nService.t("errorOccurred"), - this.i18nService.t("nameRequired"), - ); + this.toastService.showToast({ + variant: "error", + title: this.i18nService.t("errorOccurred"), + message: this.i18nService.t("nameRequired"), + }); return false; } @@ -302,22 +303,22 @@ export class AddEditComponent implements OnInit, OnDestroy { const fileEl = document.getElementById("file") as HTMLInputElement; const files = fileEl.files; if (files == null || files.length === 0) { - this.platformUtilsService.showToast( - "error", - this.i18nService.t("errorOccurred"), - this.i18nService.t("selectFile"), - ); + this.toastService.showToast({ + variant: "error", + title: this.i18nService.t("errorOccurred"), + message: this.i18nService.t("selectFile"), + }); return; } file = files[0]; if (files[0].size > 524288000) { // 500 MB - this.platformUtilsService.showToast( - "error", - this.i18nService.t("errorOccurred"), - this.i18nService.t("maxFileSize"), - ); + this.toastService.showToast({ + variant: "error", + title: this.i18nService.t("errorOccurred"), + message: this.i18nService.t("maxFileSize"), + }); return; } } @@ -340,11 +341,11 @@ export class AddEditComponent implements OnInit, OnDestroy { await this.handleCopyLinkToClipboard(); return; } - this.platformUtilsService.showToast( - "success", - null, - this.i18nService.t(this.editMode ? "editedSend" : "createdSend"), - ); + this.toastService.showToast({ + variant: "success", + title: null, + message: this.i18nService.t(this.editMode ? "editedSend" : "createdSend"), + }); }); try { await this.formPromise; @@ -377,7 +378,11 @@ export class AddEditComponent implements OnInit, OnDestroy { try { this.deletePromise = this.sendApiService.delete(this.send.id); await this.deletePromise; - this.platformUtilsService.showToast("success", null, this.i18nService.t("deletedSend")); + this.toastService.showToast({ + variant: "success", + title: null, + message: this.i18nService.t("deletedSend"), + }); await this.load(); this.onDeletedSend.emit(this.send); return true; @@ -470,11 +475,11 @@ export class AddEditComponent implements OnInit, OnDestroy { private async handleCopyLinkToClipboard() { const copySuccess = await this.copyLinkToClipboard(this.link); if (copySuccess ?? true) { - this.platformUtilsService.showToast( - "success", - null, - this.i18nService.t(this.editMode ? "editedSend" : "createdSend"), - ); + this.toastService.showToast({ + variant: "success", + title: null, + message: this.i18nService.t(this.editMode ? "editedSend" : "createdSend"), + }); } else { await this.dialogService.openSimpleDialog({ title: "", diff --git a/libs/angular/src/tools/send/send.component.ts b/libs/angular/src/tools/send/send.component.ts index fc51e324164..f331a320cd3 100644 --- a/libs/angular/src/tools/send/send.component.ts +++ b/libs/angular/src/tools/send/send.component.ts @@ -20,7 +20,7 @@ import { SendType } from "@bitwarden/common/tools/send/enums/send-type"; import { SendView } from "@bitwarden/common/tools/send/models/view/send.view"; import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service.abstraction"; import { SendService } from "@bitwarden/common/tools/send/services/send.service.abstraction"; -import { DialogService } from "@bitwarden/components"; +import { DialogService, ToastService } from "@bitwarden/components"; @Directive() export class SendComponent implements OnInit, OnDestroy { @@ -76,6 +76,7 @@ export class SendComponent implements OnInit, OnDestroy { private logService: LogService, protected sendApiService: SendApiService, protected dialogService: DialogService, + protected toastService: ToastService, ) {} async ngOnInit() { @@ -186,7 +187,11 @@ export class SendComponent implements OnInit, OnDestroy { this.onSuccessfulRemovePassword(); } else { // Default actions - this.platformUtilsService.showToast("success", null, this.i18nService.t("removedPassword")); + this.toastService.showToast({ + variant: "success", + title: null, + message: this.i18nService.t("removedPassword"), + }); await this.load(); } } catch (e) { @@ -220,7 +225,11 @@ export class SendComponent implements OnInit, OnDestroy { this.onSuccessfulDelete(); } else { // Default actions - this.platformUtilsService.showToast("success", null, this.i18nService.t("deletedSend")); + this.toastService.showToast({ + variant: "success", + title: null, + message: this.i18nService.t("deletedSend"), + }); await this.refresh(); } } catch (e) { @@ -234,11 +243,11 @@ export class SendComponent implements OnInit, OnDestroy { const env = await firstValueFrom(this.environmentService.environment$); const link = env.getSendUrl() + s.accessId + "/" + s.urlB64Key; this.platformUtilsService.copyToClipboard(link); - this.platformUtilsService.showToast( - "success", - null, - this.i18nService.t("valueCopied", this.i18nService.t("sendLink")), - ); + this.toastService.showToast({ + variant: "success", + title: null, + message: this.i18nService.t("valueCopied", this.i18nService.t("sendLink")), + }); } searchTextChanged() { diff --git a/libs/importer/src/components/import.component.ts b/libs/importer/src/components/import.component.ts index 7ae1e5c1764..8f3566831d1 100644 --- a/libs/importer/src/components/import.component.ts +++ b/libs/importer/src/components/import.component.ts @@ -48,6 +48,7 @@ import { IconButtonModule, RadioButtonModule, SelectModule, + ToastService, } from "@bitwarden/components"; import { ImportOption, ImportResult, ImportType } from "../models"; @@ -191,6 +192,7 @@ export class ImportComponent implements OnInit, OnDestroy { @Inject(ImportCollectionServiceAbstraction) @Optional() protected importCollectionService: ImportCollectionServiceAbstraction, + protected toastService: ToastService, ) {} protected get importBlockedByPolicy(): boolean { @@ -336,22 +338,22 @@ export class ImportComponent implements OnInit, OnDestroy { ); if (importer === null) { - this.platformUtilsService.showToast( - "error", - this.i18nService.t("errorOccurred"), - this.i18nService.t("selectFormat"), - ); + this.toastService.showToast({ + variant: "error", + title: this.i18nService.t("errorOccurred"), + message: this.i18nService.t("selectFormat"), + }); return; } const importContents = await this.setImportContents(); if (importContents == null || importContents === "") { - this.platformUtilsService.showToast( - "error", - this.i18nService.t("errorOccurred"), - this.i18nService.t("selectFile"), - ); + this.toastService.showToast({ + variant: "error", + title: this.i18nService.t("errorOccurred"), + message: this.i18nService.t("selectFile"), + }); return; } @@ -502,11 +504,11 @@ export class ImportComponent implements OnInit, OnDestroy { } if (this.importBlockedByPolicy && this.organizationId == null) { - this.platformUtilsService.showToast( - "error", - null, - this.i18nService.t("personalOwnershipPolicyInEffectImports"), - ); + this.toastService.showToast({ + variant: "error", + title: null, + message: this.i18nService.t("personalOwnershipPolicyInEffectImports"), + }); return false; } @@ -517,14 +519,6 @@ export class ImportComponent implements OnInit, OnDestroy { const fileEl = document.getElementById("import_input_file") as HTMLInputElement; const files = fileEl.files; let fileContents = this.formGroup.controls.fileContents.value; - if ((files == null || files.length === 0) && (fileContents == null || fileContents === "")) { - this.platformUtilsService.showToast( - "error", - this.i18nService.t("errorOccurred"), - this.i18nService.t("selectFile"), - ); - return; - } if (files != null && files.length > 0) { try { diff --git a/libs/tools/export/vault-export/vault-export-ui/src/components/export.component.ts b/libs/tools/export/vault-export/vault-export-ui/src/components/export.component.ts index 0d0ec92eac7..9f81f5e5502 100644 --- a/libs/tools/export/vault-export/vault-export-ui/src/components/export.component.ts +++ b/libs/tools/export/vault-export/vault-export-ui/src/components/export.component.ts @@ -15,7 +15,6 @@ import { EventType } from "@bitwarden/common/enums"; import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; -import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { EncryptedExportType } from "@bitwarden/common/tools/enums/encrypted-export-type.enum"; import { @@ -28,6 +27,7 @@ import { IconButtonModule, RadioButtonModule, SelectModule, + ToastService, } from "@bitwarden/components"; import { VaultExportServiceAbstraction } from "@bitwarden/vault-export-core"; @@ -123,7 +123,7 @@ export class ExportComponent implements OnInit, OnDestroy { constructor( protected i18nService: I18nService, - protected platformUtilsService: PlatformUtilsService, + protected toastService: ToastService, protected exportService: VaultExportServiceAbstraction, protected eventCollectionService: EventCollectionService, private policyService: PolicyService, @@ -222,11 +222,11 @@ export class ExportComponent implements OnInit, OnDestroy { submit = async () => { if (this.isFileEncryptedExport && this.filePassword != this.confirmFilePassword) { - this.platformUtilsService.showToast( - "error", - this.i18nService.t("errorOccurred"), - this.i18nService.t("filePasswordAndConfirmFilePasswordDoNotMatch"), - ); + this.toastService.showToast({ + variant: "error", + title: this.i18nService.t("errorOccurred"), + message: this.i18nService.t("filePasswordAndConfirmFilePasswordDoNotMatch"), + }); return; } @@ -236,11 +236,11 @@ export class ExportComponent implements OnInit, OnDestroy { } if (this.disabledByPolicy) { - this.platformUtilsService.showToast( - "error", - null, - this.i18nService.t("personalVaultExportPolicyInEffect"), - ); + this.toastService.showToast({ + variant: "error", + title: null, + message: this.i18nService.t("personalVaultExportPolicyInEffect"), + }); return; } From 593dc3c71660df0f5e7add2b7d53dd9083b1fe04 Mon Sep 17 00:00:00 2001 From: cyprain-okeke <108260115+cyprain-okeke@users.noreply.github.com> Date: Thu, 20 Jun 2024 13:50:10 +0100 Subject: [PATCH 7/7] [AC-2721] [Defect] Apply subscription status updates in provider subscription details (#9729) * Resolve the pending cancellation status * Add the contact information --- apps/web/src/locales/en/messages.json | 3 +++ .../subscription/subscription-status.component.ts | 12 +++++++++--- .../response/provider-subscription-response.ts | 2 ++ 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/apps/web/src/locales/en/messages.json b/apps/web/src/locales/en/messages.json index 18b45f4d458..82aee1e3200 100644 --- a/apps/web/src/locales/en/messages.json +++ b/apps/web/src/locales/en/messages.json @@ -8426,5 +8426,8 @@ "example":"increments of 100,000" } } + }, + "providerReinstate":{ + "message": " Contact Customer Support to reinstate your subscription." } } diff --git a/bitwarden_license/bit-web/src/app/billing/providers/subscription/subscription-status.component.ts b/bitwarden_license/bit-web/src/app/billing/providers/subscription/subscription-status.component.ts index fa9a892254e..91cdef10aca 100644 --- a/bitwarden_license/bit-web/src/app/billing/providers/subscription/subscription-status.component.ts +++ b/bitwarden_license/bit-web/src/app/billing/providers/subscription/subscription-status.component.ts @@ -43,6 +43,10 @@ export class SubscriptionStatusComponent { } get status(): string { + if (this.subscription.cancelAt && this.subscription.status === "active") { + this.subscription.status = "pending_cancellation"; + } + return this.subscription.status; } @@ -151,13 +155,15 @@ export class SubscriptionStatusComponent { }, date: { label: cancellationDateLabel, - value: this.subscription.currentPeriodEndDate.toDateString(), + value: this.subscription.cancelAt, }, callout: { severity: "warning", header: pendingCancellationText, - body: this.i18nService.t("subscriptionPendingCanceled"), - showReinstatementButton: true, + body: + this.i18nService.t("subscriptionPendingCanceled") + + this.i18nService.t("providerReinstate"), + showReinstatementButton: false, }, }; } diff --git a/libs/common/src/billing/models/response/provider-subscription-response.ts b/libs/common/src/billing/models/response/provider-subscription-response.ts index fb8818fb39d..4986914cc0b 100644 --- a/libs/common/src/billing/models/response/provider-subscription-response.ts +++ b/libs/common/src/billing/models/response/provider-subscription-response.ts @@ -9,6 +9,7 @@ export class ProviderSubscriptionResponse extends BaseResponse { unpaidPeriodEndDate?: string; gracePeriod?: number | null; suspensionDate?: string; + cancelAt?: string; constructor(response: any) { super(response); @@ -19,6 +20,7 @@ export class ProviderSubscriptionResponse extends BaseResponse { this.unpaidPeriodEndDate = this.getResponseProperty("unpaidPeriodEndDate"); this.gracePeriod = this.getResponseProperty("gracePeriod"); this.suspensionDate = this.getResponseProperty("suspensionDate"); + this.cancelAt = this.getResponseProperty("cancelAt"); const plans = this.getResponseProperty("plans"); if (plans != null) { this.plans = plans.map((i: any) => new ProviderPlanResponse(i));