1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-12 22:44:11 +00:00

Merge branch 'main' into ps/extension-refresh

This commit is contained in:
Victoria League
2024-11-06 10:52:07 -05:00
committed by GitHub
48 changed files with 221 additions and 228 deletions

View File

@@ -1,10 +1,12 @@
import { Component, OnDestroy, OnInit, ViewChild } from "@angular/core";
import { FormBuilder, Validators } from "@angular/forms";
import { Router } from "@angular/router";
import { Subject, firstValueFrom, switchMap, takeUntil } from "rxjs";
import { ActivatedRoute, Router } from "@angular/router";
import { Subject, firstValueFrom, switchMap, takeUntil, tap } from "rxjs";
import { EnvironmentSelectorComponent } from "@bitwarden/angular/auth/components/environment-selector.component";
import { LoginEmailServiceAbstraction, RegisterRouteService } from "@bitwarden/auth/common";
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { ToastService } from "@bitwarden/components";
@@ -38,9 +40,13 @@ export class HomeComponent implements OnInit, OnDestroy {
private accountSwitcherService: AccountSwitcherService,
private registerRouteService: RegisterRouteService,
private toastService: ToastService,
private configService: ConfigService,
private route: ActivatedRoute,
) {}
async ngOnInit(): Promise<void> {
this.listenForUnauthUiRefreshFlagChanges();
const email = await firstValueFrom(this.loginEmailService.loginEmail$);
const rememberEmail = this.loginEmailService.getRememberEmail();
@@ -70,6 +76,29 @@ export class HomeComponent implements OnInit, OnDestroy {
this.destroyed$.complete();
}
private listenForUnauthUiRefreshFlagChanges() {
this.configService
.getFeatureFlag$(FeatureFlag.UnauthenticatedExtensionUIRefresh)
.pipe(
tap(async (flag) => {
// If the flag is turned ON, we must force a reload to ensure the correct UI is shown
if (flag) {
const uniqueQueryParams = {
...this.route.queryParams,
// adding a unique timestamp to the query params to force a reload
t: new Date().getTime().toString(),
};
await this.router.navigate(["/login"], {
queryParams: uniqueQueryParams,
});
}
}),
takeUntil(this.destroyed$),
)
.subscribe();
}
get availableAccounts$() {
return this.accountSwitcherService.availableAccounts$;
}

View File

@@ -39,9 +39,9 @@ dependencies = [
[[package]]
name = "anyhow"
version = "1.0.86"
version = "1.0.93"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775"
[[package]]
name = "arboard"
@@ -1154,9 +1154,9 @@ dependencies = [
[[package]]
name = "napi"
version = "2.16.11"
version = "2.16.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53575dfa17f208dd1ce3a2da2da4659aae393b256a472f2738a8586a6c4107fd"
checksum = "214f07a80874bb96a8433b3cdfc84980d56c7b02e1a0d7ba4ba0db5cef785e2b"
dependencies = [
"bitflags",
"ctor",
@@ -1867,18 +1867,18 @@ dependencies = [
[[package]]
name = "thiserror"
version = "1.0.61"
version = "1.0.68"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709"
checksum = "02dd99dc800bbb97186339685293e1cc5d9df1f8fae2d0aecd9ff1c77efea892"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.61"
version = "1.0.68"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533"
checksum = "a7c61ec9a6f64d2793d8a45faba21efbe3ced62a886d44c36a009b2b519b4c7e"
dependencies = [
"proc-macro2",
"quote",
@@ -2487,9 +2487,9 @@ dependencies = [
[[package]]
name = "zbus"
version = "4.3.1"
version = "4.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "851238c133804e0aa888edf4a0229481c753544ca12a60fd1c3230c8a500fe40"
checksum = "bb97012beadd29e654708a0fdb4c84bc046f537aecfde2c3ee0a9e4b4d48c725"
dependencies = [
"async-broadcast",
"async-executor",
@@ -2525,9 +2525,9 @@ dependencies = [
[[package]]
name = "zbus_macros"
version = "4.3.1"
version = "4.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d5a3f12c20bd473be3194af6b49d50d7bb804ef3192dc70eddedb26b85d9da7"
checksum = "267db9407081e90bbfa46d841d3cbc60f59c0351838c4bc65199ecd79ab1983e"
dependencies = [
"proc-macro-crate",
"proc-macro2",
@@ -2583,9 +2583,9 @@ dependencies = [
[[package]]
name = "zvariant"
version = "4.1.2"
version = "4.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1724a2b330760dc7d2a8402d841119dc869ef120b139d29862d6980e9c75bfc9"
checksum = "2084290ab9a1c471c38fc524945837734fbf124487e105daec2bb57fd48c81fe"
dependencies = [
"endi",
"enumflags2",
@@ -2596,9 +2596,9 @@ dependencies = [
[[package]]
name = "zvariant_derive"
version = "4.1.2"
version = "4.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55025a7a518ad14518fb243559c058a2e5b848b015e31f1d90414f36e3317859"
checksum = "73e2ba546bda683a90652bac4a279bc146adad1386f25379cf73200d2002c449"
dependencies = [
"proc-macro-crate",
"proc-macro2",
@@ -2609,9 +2609,9 @@ dependencies = [
[[package]]
name = "zvariant_utils"
version = "2.0.0"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc242db087efc22bd9ade7aa7809e4ba828132edc312871584a6b4391bdf8786"
checksum = "c51bcff7cc3dbb5055396bcf774748c3dab426b4b8659046963523cee4808340"
dependencies = [
"proc-macro2",
"quote",

View File

@@ -23,7 +23,7 @@ sys = [
[dependencies]
aes = "=0.8.4"
anyhow = "=1.0.86"
anyhow = "=1.0.93"
arboard = { version = "=3.4.1", default-features = false, features = [
"wayland-data-control",
] }
@@ -38,7 +38,7 @@ rand = "=0.8.5"
retry = "=2.0.0"
scopeguard = "=1.2.0"
sha2 = "=0.10.8"
thiserror = "=1.0.61"
thiserror = "=1.0.68"
tokio = { version = "=1.41.0", features = ["io-util", "sync", "macros"] }
tokio-util = "=0.7.12"
typenum = "=1.17.0"
@@ -68,5 +68,5 @@ security-framework-sys = { version = "=2.11.0", optional = true }
[target.'cfg(target_os = "linux")'.dependencies]
gio = { version = "=0.19.5", optional = true }
libsecret = { version = "=0.5.0", optional = true }
zbus = { version = "=4.3.1", optional = true }
zbus = { version = "=4.4.0", optional = true }
zbus_polkit = { version = "=4.0.0", optional = true }

View File

@@ -14,9 +14,9 @@ default = []
manual_test = []
[dependencies]
anyhow = "=1.0.86"
anyhow = "=1.0.93"
desktop_core = { path = "../core" }
napi = { version = "=2.16.11", features = ["async"] }
napi = { version = "=2.16.13", features = ["async"] }
napi-derive = "=2.16.12"
tokio = { version = "1.38.0" }
tokio-util = "0.7.11"

View File

@@ -7,7 +7,7 @@ version = "0.0.0"
publish = false
[dependencies]
anyhow = "=1.0.86"
anyhow = "=1.0.93"
desktop_core = { path = "../core", default-features = false }
futures = "0.3.30"
log = "0.4.22"

View File

@@ -1,7 +1,7 @@
import { Component, NgZone, OnDestroy, OnInit, ViewChild, ViewContainerRef } from "@angular/core";
import { FormBuilder } from "@angular/forms";
import { ActivatedRoute, Router } from "@angular/router";
import { Subject, takeUntil } from "rxjs";
import { Subject, takeUntil, tap } from "rxjs";
import { LoginComponentV1 as BaseLoginComponent } from "@bitwarden/angular/auth/components/login-v1.component";
import { FormValidationErrorsService } from "@bitwarden/angular/platform/abstractions/form-validation-errors.service";
@@ -14,8 +14,10 @@ import {
import { DevicesApiServiceAbstraction } from "@bitwarden/common/auth/abstractions/devices-api.service.abstraction";
import { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction";
import { WebAuthnLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/webauthn/webauthn-login.service.abstraction";
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service";
import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service";
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
@@ -76,6 +78,7 @@ export class LoginComponentV1 extends BaseLoginComponent implements OnInit, OnDe
webAuthnLoginService: WebAuthnLoginServiceAbstraction,
registerRouteService: RegisterRouteService,
toastService: ToastService,
private configService: ConfigService,
) {
super(
devicesApiService,
@@ -105,6 +108,8 @@ export class LoginComponentV1 extends BaseLoginComponent implements OnInit, OnDe
}
async ngOnInit() {
this.listenForUnauthUiRefreshFlagChanges();
await super.ngOnInit();
await this.getLoginWithDevice(this.loggedEmail);
this.broadcasterService.subscribe(BroadcasterSubscriptionId, async (message: any) => {
@@ -137,6 +142,29 @@ export class LoginComponentV1 extends BaseLoginComponent implements OnInit, OnDe
this.componentDestroyed$.complete();
}
private listenForUnauthUiRefreshFlagChanges() {
this.configService
.getFeatureFlag$(FeatureFlag.UnauthenticatedExtensionUIRefresh)
.pipe(
tap(async (flag) => {
// If the flag is turned ON, we must force a reload to ensure the correct UI is shown
if (flag) {
const uniqueQueryParams = {
...this.route.queryParams,
// adding a unique timestamp to the query params to force a reload
t: new Date().getTime().toString(),
};
await this.router.navigate(["/"], {
queryParams: uniqueQueryParams,
});
}
}),
takeUntil(this.destroy$),
)
.subscribe();
}
async settings() {
const [modal, childComponent] = await this.modalService.openViewRef(
EnvironmentComponent,

View File

@@ -40,9 +40,9 @@
></bit-nav-item>
</bit-nav-group>
<bit-nav-item
*ngIf="isAccessIntelligenceFeatureEnabled"
[text]="'accessIntelligence' | i18n"
route="access-intelligence"
*ngIf="isRiskInsightsFeatureEnabled"
[text]="'riskInsights' | i18n"
route="risk-insights"
></bit-nav-item>
<bit-nav-group
icon="bwi-billing"

View File

@@ -51,7 +51,7 @@ export class OrganizationLayoutComponent implements OnInit, OnDestroy {
showPaymentAndHistory$: Observable<boolean>;
hideNewOrgButton$: Observable<boolean>;
organizationIsUnmanaged$: Observable<boolean>;
isAccessIntelligenceFeatureEnabled = false;
isRiskInsightsFeatureEnabled = false;
private _destroy = new Subject<void>();
@@ -71,7 +71,7 @@ export class OrganizationLayoutComponent implements OnInit, OnDestroy {
async ngOnInit() {
document.body.classList.remove("layout_frontend");
this.isAccessIntelligenceFeatureEnabled = await this.configService.getFeatureFlag(
this.isRiskInsightsFeatureEnabled = await this.configService.getFeatureFlag(
FeatureFlag.AccessIntelligence,
);

View File

@@ -63,10 +63,10 @@ const routes: Routes = [
),
},
{
path: "access-intelligence",
path: "risk-insights",
loadChildren: () =>
import("../../tools/access-intelligence/access-intelligence.module").then(
(m) => m.AccessIntelligenceModule,
import("../../tools/risk-insights/risk-insights.module").then(
(m) => m.RiskInsightsModule,
),
},
{

View File

@@ -48,16 +48,7 @@
}}</span>
</dd>
<dt>{{ "nextCharge" | i18n }}</dt>
<dd *ngIf="!enableTimeThreshold">
{{
nextInvoice
? (nextInvoice.date | date: "mediumDate") +
", " +
(nextInvoice.amount | currency: "$")
: "-"
}}
</dd>
<dd *ngIf="enableTimeThreshold">
<dd>
{{
nextInvoice
? (sub.subscription.periodEndDate | date: "mediumDate") +

View File

@@ -38,13 +38,9 @@ export class UserSubscriptionComponent implements OnInit {
sub: SubscriptionResponse;
selfHosted = false;
cloudWebVaultUrl: string;
enableTimeThreshold: boolean;
cancelPromise: Promise<any>;
reinstatePromise: Promise<any>;
protected enableTimeThreshold$ = this.configService.getFeatureFlag$(
FeatureFlag.EnableTimeThreshold,
);
protected deprecateStripeSourcesAPI$ = this.configService.getFeatureFlag$(
FeatureFlag.AC2476_DeprecateStripeSourcesAPI,
@@ -69,7 +65,6 @@ export class UserSubscriptionComponent implements OnInit {
async ngOnInit() {
this.cloudWebVaultUrl = await firstValueFrom(this.environmentService.cloudWebVaultUrl$);
await this.load();
this.enableTimeThreshold = await firstValueFrom(this.enableTimeThreshold$);
this.firstLoaded = true;
}

View File

@@ -48,10 +48,7 @@
<dt [ngClass]="{ 'tw-text-danger': isExpired }">
{{ "subscriptionExpiration" | i18n }}
</dt>
<dd [ngClass]="{ 'tw-text-danger': isExpired }" *ngIf="!enableTimeThreshold">
{{ nextInvoice ? (nextInvoice.date | date: "mediumDate") : "-" }}
</dd>
<dd [ngClass]="{ 'tw-text-danger': isExpired }" *ngIf="enableTimeThreshold">
<dd [ngClass]="{ 'tw-text-danger': isExpired }">
{{ nextInvoice ? (sub.subscription.periodEndDate | date: "mediumDate") : "-" }}
</dd>
</ng-container>

View File

@@ -52,7 +52,6 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy
loading = true;
locale: string;
showUpdatedSubscriptionStatusSection$: Observable<boolean>;
enableTimeThreshold: boolean;
preSelectedProductTier: ProductTierType = ProductTierType.Free;
showSubscription = true;
showSelfHost = false;
@@ -65,10 +64,6 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy
FeatureFlag.EnableConsolidatedBilling,
);
protected enableTimeThreshold$ = this.configService.getFeatureFlag$(
FeatureFlag.EnableTimeThreshold,
);
protected enableUpgradePasswordManagerSub$ = this.configService.getFeatureFlag$(
FeatureFlag.EnableUpgradePasswordManagerSub,
);
@@ -117,7 +112,6 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy
this.showUpdatedSubscriptionStatusSection$ = this.configService.getFeatureFlag$(
FeatureFlag.AC1795_UpdatedSubscriptionStatusSection,
);
this.enableTimeThreshold = await firstValueFrom(this.enableTimeThreshold$);
}
ngOnDestroy() {
@@ -298,9 +292,6 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy
return this.i18nService.t("subscriptionUpgrade", this.sub.seats.toString());
}
} else if (this.sub.maxAutoscaleSeats === this.sub.seats && this.sub.seats != null) {
if (!this.enableTimeThreshold) {
return this.i18nService.t("subscriptionMaxReached", this.sub.seats.toString());
}
const seatAdjustmentMessage = this.sub.plan.isAnnual
? "annualSubscriptionUserSeatsMessage"
: "monthlySubscriptionUserSeatsMessage";
@@ -311,21 +302,11 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy
} else if (this.userOrg.productTierType === ProductTierType.TeamsStarter) {
return this.i18nService.t("subscriptionUserSeatsWithoutAdditionalSeatsOption", 10);
} else if (this.sub.maxAutoscaleSeats == null) {
if (!this.enableTimeThreshold) {
return this.i18nService.t("subscriptionUserSeatsUnlimitedAutoscale");
}
const seatAdjustmentMessage = this.sub.plan.isAnnual
? "annualSubscriptionUserSeatsMessage"
: "monthlySubscriptionUserSeatsMessage";
return this.i18nService.t(seatAdjustmentMessage);
} else {
if (!this.enableTimeThreshold) {
return this.i18nService.t(
"subscriptionUserSeatsLimitedAutoscale",
this.sub.maxAutoscaleSeats.toString(),
);
}
const seatAdjustmentMessage = this.sub.plan.isAnnual
? "annualSubscriptionUserSeatsMessage"
: "monthlySubscriptionUserSeatsMessage";

View File

@@ -1,9 +0,0 @@
import { NgModule } from "@angular/core";
import { AccessIntelligenceRoutingModule } from "./access-intelligence-routing.module";
import { AccessIntelligenceComponent } from "./access-intelligence.component";
@NgModule({
imports: [AccessIntelligenceComponent, AccessIntelligenceRoutingModule],
})
export class AccessIntelligenceModule {}

View File

@@ -1,120 +0,0 @@
<p>{{ "passwordsReportDesc" | i18n }}</p>
<div *ngIf="loading">
<i
class="bwi bwi-spinner bwi-spin tw-text-muted"
title="{{ 'loading' | i18n }}"
aria-hidden="true"
></i>
<span class="tw-sr-only">{{ "loading" | i18n }}</span>
</div>
<div class="tw-mt-4 tw-flex tw-flex-col" *ngIf="!loading && dataSource.data.length">
<div class="tw-flex tw-gap-6">
<tools-card
class="tw-flex-1"
[title]="'atRiskMembers' | i18n"
[value]="totalMembersMap.size - 3"
[maxValue]="totalMembersMap.size"
>
</tools-card>
<tools-card
class="tw-flex-1"
[title]="'atRiskApplications' | i18n"
[value]="totalMembersMap.size - 1"
[maxValue]="totalMembersMap.size"
>
</tools-card>
</div>
<div class="tw-flex tw-mt-8 tw-mb-4 tw-gap-4">
<bit-search class="tw-grow" [formControl]="searchControl"></bit-search>
<button class="tw-rounded-lg" type="button" buttonType="secondary" bitButton>
<i class="bwi bwi-star-f tw-mr-2"></i>
{{ "markAppAsCritical" | i18n }}
</button>
</div>
<div class="tw-mt-4 tw-flex tw-flex-col" *ngIf="!loading && dataSource.data.length">
<div class="tw-flex tw-gap-6">
<tools-card
class="tw-flex-1"
[title]="'atRiskMembers' | i18n"
[value]="totalMembersMap.size - 3"
[maxValue]="totalMembersMap.size"
>
</tools-card>
<tools-card
class="tw-flex-1"
[title]="'atRiskApplications' | i18n"
[value]="totalMembersMap.size - 1"
[maxValue]="totalMembersMap.size"
>
</tools-card>
</div>
<div class="tw-flex tw-mt-8 tw-mb-4 tw-gap-4">
<bit-search class="tw-grow" [formControl]="searchControl"></bit-search>
<button
class="tw-rounded-lg"
type="button"
buttonType="secondary"
[disabled]="!selectedIds.size"
bitButton
[bitAction]="markAppsAsCritical"
appA11yTitle="{{ 'markAppAsCritical' | i18n }}"
>
<i class="bwi bwi-star-f tw-mr-2"></i>
{{ "markAppAsCritical" | i18n }}
</button>
</div>
<bit-table [dataSource]="dataSource">
<ng-container header>
<tr bitRow>
<th bitCell></th>
<th bitCell bitSortable="name">{{ "name" | i18n }}</th>
<th bitCell class="tw-text-right">{{ "weakness" | i18n }}</th>
<th bitCell class="tw-text-right">{{ "timesReused" | i18n }}</th>
<th bitCell class="tw-text-right">{{ "timesExposed" | i18n }}</th>
<th bitCell class="tw-text-right">{{ "totalMembers" | i18n }}</th>
</tr>
</ng-container>
<ng-template body let-rows$>
<tr bitRow *ngFor="let r of rows$ | async; trackBy: trackByFunction">
<td bitCell>
<input
bitCheckbox
type="checkbox"
[checked]="selectedIds.has(r.id)"
(change)="onCheckboxChange(r.id, $event)"
/>
</td>
<td bitCell>
<ng-container>
<span>{{ r.name }}</span>
</ng-container>
<br />
<small>{{ r.subTitle }}</small>
</td>
<td bitCell class="tw-text-right">
<span
bitBadge
*ngIf="passwordStrengthMap.has(r.id)"
[variant]="passwordStrengthMap.get(r.id)[1]"
>
{{ passwordStrengthMap.get(r.id)[0] | i18n }}
</span>
</td>
<td bitCell class="tw-text-right">
<span bitBadge *ngIf="passwordUseMap.has(r.login.password)" variant="warning">
{{ "reusedXTimes" | i18n: passwordUseMap.get(r.login.password) }}
</span>
</td>
<td bitCell class="tw-text-right">
<span bitBadge *ngIf="exposedPasswordMap.has(r.id)" variant="warning">
{{ "exposedXTimes" | i18n: exposedPasswordMap.get(r.id) }}
</span>
</td>
<td bitCell class="tw-text-right" data-testid="total-membership">
{{ totalMembersMap.get(r.id) || 0 }}
</td>
</tr>
</ng-template>
</bit-table>
</div>
</div>

View File

@@ -12,8 +12,8 @@ import { HeaderModule } from "../../layouts/header/header.module";
import { SharedModule } from "../../shared";
import { PipesModule } from "../../vault/individual-vault/pipes/pipes.module";
import { AccessIntelligenceTabType } from "./access-intelligence.component";
import { applicationTableMockData } from "./application-table.mock";
import { RiskInsightsTabType } from "./risk-insights.component";
@Component({
standalone: true,
@@ -49,8 +49,8 @@ export class CriticalApplicationsComponent implements OnInit {
}
goToAllAppsTab = async () => {
await this.router.navigate([`organizations/${this.organizationId}/access-intelligence`], {
queryParams: { tabIndex: AccessIntelligenceTabType.AllApps },
await this.router.navigate([`organizations/${this.organizationId}/risk-insights`], {
queryParams: { tabIndex: RiskInsightsTabType.AllApps },
queryParamsHandling: "merge",
});
};

View File

@@ -4,7 +4,7 @@ import { mock, MockProxy } from "jest-mock-extended";
import { of } from "rxjs";
// eslint-disable-next-line no-restricted-imports
import { PasswordHealthService } from "@bitwarden/bit-common/tools/reports/access-intelligence";
import { PasswordHealthService } from "@bitwarden/bit-common/tools/reports/risk-insights";
import { AuditService } from "@bitwarden/common/abstractions/audit.service";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";

View File

@@ -6,7 +6,7 @@ import { map } from "rxjs";
import { JslibModule } from "@bitwarden/angular/jslib.module";
// eslint-disable-next-line no-restricted-imports
import { PasswordHealthService } from "@bitwarden/bit-common/tools/reports/access-intelligence";
import { PasswordHealthService } from "@bitwarden/bit-common/tools/reports/risk-insights";
import { AuditService } from "@bitwarden/common/abstractions/audit.service";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";

View File

@@ -0,0 +1,64 @@
<p>{{ "passwordsReportDesc" | i18n }}</p>
<div *ngIf="loading">
<i
class="bwi bwi-spinner bwi-spin tw-text-muted"
title="{{ 'loading' | i18n }}"
aria-hidden="true"
></i>
<span class="tw-sr-only">{{ "loading" | i18n }}</span>
</div>
<div class="tw-flex tw-flex-col" *ngIf="!loading && dataSource.data.length">
<bit-table [dataSource]="dataSource">
<ng-container header>
<tr bitRow>
<th bitCell></th>
<th bitCell bitSortable="name">{{ "name" | i18n }}</th>
<th bitCell class="tw-text-right">{{ "weakness" | i18n }}</th>
<th bitCell class="tw-text-right">{{ "timesReused" | i18n }}</th>
<th bitCell class="tw-text-right">{{ "timesExposed" | i18n }}</th>
<th bitCell class="tw-text-right">{{ "totalMembers" | i18n }}</th>
</tr>
</ng-container>
<ng-template body let-rows$>
<tr bitRow *ngFor="let r of rows$ | async; trackBy: trackByFunction">
<td bitCell>
<input
bitCheckbox
type="checkbox"
[checked]="selectedIds.has(r.id)"
(change)="onCheckboxChange(r.id, $event)"
/>
</td>
<td bitCell>
<ng-container>
<span>{{ r.name }}</span>
</ng-container>
<br />
<small>{{ r.subTitle }}</small>
</td>
<td bitCell class="tw-text-right">
<span
bitBadge
*ngIf="passwordStrengthMap.has(r.id)"
[variant]="passwordStrengthMap.get(r.id)[1]"
>
{{ passwordStrengthMap.get(r.id)[0] | i18n }}
</span>
</td>
<td bitCell class="tw-text-right">
<span bitBadge *ngIf="passwordUseMap.has(r.login.password)" variant="warning">
{{ "reusedXTimes" | i18n: passwordUseMap.get(r.login.password) }}
</span>
</td>
<td bitCell class="tw-text-right">
<span bitBadge *ngIf="exposedPasswordMap.has(r.id)" variant="warning">
{{ "exposedXTimes" | i18n: exposedPasswordMap.get(r.id) }}
</span>
</td>
<td bitCell class="tw-text-right" data-testid="total-membership">
{{ totalMembersMap.get(r.id) || 0 }}
</td>
</tr>
</ng-template>
</bit-table>
</div>

View File

@@ -5,7 +5,7 @@ import { ActivatedRoute } from "@angular/router";
import { debounceTime, map } from "rxjs";
// eslint-disable-next-line no-restricted-imports
import { PasswordHealthService } from "@bitwarden/bit-common/tools/reports/access-intelligence";
import { PasswordHealthService } from "@bitwarden/bit-common/tools/reports/risk-insights";
import { AuditService } from "@bitwarden/common/abstractions/audit.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/password-strength";

View File

@@ -4,7 +4,7 @@ import { mock } from "jest-mock-extended";
import { of } from "rxjs";
// eslint-disable-next-line no-restricted-imports
import { PasswordHealthService } from "@bitwarden/bit-common/tools/reports/access-intelligence";
import { PasswordHealthService } from "@bitwarden/bit-common/tools/reports/risk-insights";
import { AuditService } from "@bitwarden/common/abstractions/audit.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/password-strength";

View File

@@ -6,7 +6,7 @@ import { map } from "rxjs";
import { JslibModule } from "@bitwarden/angular/jslib.module";
// eslint-disable-next-line no-restricted-imports
import { PasswordHealthService } from "@bitwarden/bit-common/tools/reports/access-intelligence";
import { PasswordHealthService } from "@bitwarden/bit-common/tools/reports/risk-insights";
import { AuditService } from "@bitwarden/common/abstractions/audit.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/password-strength";

View File

@@ -4,15 +4,15 @@ import { RouterModule, Routes } from "@angular/router";
import { canAccessFeature } from "@bitwarden/angular/platform/guard/feature-flag.guard";
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { AccessIntelligenceComponent } from "./access-intelligence.component";
import { RiskInsightsComponent } from "./risk-insights.component";
const routes: Routes = [
{
path: "",
component: AccessIntelligenceComponent,
component: RiskInsightsComponent,
canActivate: [canAccessFeature(FeatureFlag.AccessIntelligence)],
data: {
titleId: "accessIntelligence",
titleId: "RiskInsights",
},
},
];
@@ -21,4 +21,4 @@ const routes: Routes = [
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
})
export class AccessIntelligenceRoutingModule {}
export class RiskInsightsRoutingModule {}

View File

@@ -1,4 +1,4 @@
<div class="tw-mb-1 text-primary" bitTypography="body1">{{ "accessIntelligence" | i18n }}</div>
<div class="tw-mb-1 text-primary" bitTypography="body1">{{ "riskInsights" | i18n }}</div>
<h1 bitTypography="h1">{{ "passwordRisk" | i18n }}</h1>
<div class="tw-text-muted">{{ "discoverAtRiskPasswords" | i18n }}</div>
<div class="tw-bg-primary-100 tw-rounded-lg tw-w-full tw-px-8 tw-py-2 tw-my-4">

View File

@@ -15,7 +15,7 @@ import { PasswordHealthMembersURIComponent } from "./password-health-members-uri
import { PasswordHealthMembersComponent } from "./password-health-members.component";
import { PasswordHealthComponent } from "./password-health.component";
export enum AccessIntelligenceTabType {
export enum RiskInsightsTabType {
AllApps = 0,
CriticalApps = 1,
NotifiedMembers = 2,
@@ -23,7 +23,7 @@ export enum AccessIntelligenceTabType {
@Component({
standalone: true,
templateUrl: "./access-intelligence.component.html",
templateUrl: "./risk-insights.component.html",
imports: [
AllApplicationsComponent,
AsyncActionsModule,
@@ -39,8 +39,8 @@ export enum AccessIntelligenceTabType {
TabsModule,
],
})
export class AccessIntelligenceComponent {
tabIndex: AccessIntelligenceTabType;
export class RiskInsightsComponent {
tabIndex: RiskInsightsTabType;
dataLastUpdated = new Date();
apps: any[] = [];
@@ -70,7 +70,7 @@ export class AccessIntelligenceComponent {
private router: Router,
) {
route.queryParams.pipe(takeUntilDestroyed()).subscribe(({ tabIndex }) => {
this.tabIndex = !isNaN(tabIndex) ? tabIndex : AccessIntelligenceTabType.AllApps;
this.tabIndex = !isNaN(tabIndex) ? tabIndex : RiskInsightsTabType.AllApps;
});
}
}

View File

@@ -0,0 +1,9 @@
import { NgModule } from "@angular/core";
import { RiskInsightsRoutingModule } from "./risk-insights-routing.module";
import { RiskInsightsComponent } from "./risk-insights.component";
@NgModule({
imports: [RiskInsightsComponent, RiskInsightsRoutingModule],
})
export class RiskInsightsModule {}

View File

@@ -5,8 +5,8 @@
"criticalApplications": {
"message": "Critical applications"
},
"accessIntelligence": {
"message": "Access Intelligence"
"riskInsights": {
"message": "Risk Insights"
},
"passwordRisk": {
"message": "Password Risk"

View File

@@ -1,9 +1,9 @@
import { Inject, Injectable } from "@angular/core";
// eslint-disable-next-line no-restricted-imports
import { mockCiphers } from "@bitwarden/bit-common/tools/reports/access-intelligence/services/ciphers.mock";
import { mockCiphers } from "@bitwarden/bit-common/tools/reports/risk-insights/services/ciphers.mock";
// eslint-disable-next-line no-restricted-imports
import { mockMemberCipherDetailsResponse } from "@bitwarden/bit-common/tools/reports/access-intelligence/services/member-cipher-details-response.mock";
import { mockMemberCipherDetailsResponse } from "@bitwarden/bit-common/tools/reports/risk-insights/services/member-cipher-details-response.mock";
import { AuditService } from "@bitwarden/common/abstractions/audit.service";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/password-strength";

View File

@@ -2,7 +2,7 @@ import { CommonModule } from "@angular/common";
import { Component, ElementRef, Input, NgZone, OnDestroy, OnInit, ViewChild } from "@angular/core";
import { FormBuilder, FormControl, ReactiveFormsModule, Validators } from "@angular/forms";
import { ActivatedRoute, Router, RouterModule } from "@angular/router";
import { firstValueFrom, Subject, take, takeUntil } from "rxjs";
import { firstValueFrom, Subject, take, takeUntil, tap } from "rxjs";
import { JslibModule } from "@bitwarden/angular/jslib.module";
import {
@@ -19,9 +19,11 @@ import { CaptchaIFrame } from "@bitwarden/common/auth/captcha-iframe";
import { AuthResult } from "@bitwarden/common/auth/models/domain/auth-result";
import { ForceSetPasswordReason } from "@bitwarden/common/auth/models/domain/force-set-password-reason";
import { ClientType, HttpStatusCode } from "@bitwarden/common/enums";
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { ErrorResponse } from "@bitwarden/common/models/response/error.response";
import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service";
import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
@@ -139,12 +141,16 @@ export class LoginComponent implements OnInit, OnDestroy {
private toastService: ToastService,
private logService: LogService,
private validationService: ValidationService,
private configService: ConfigService,
) {
this.clientType = this.platformUtilsService.getClientType();
this.loginViaAuthRequestSupported = this.loginComponentService.isLoginViaAuthRequestSupported();
}
async ngOnInit(): Promise<void> {
// TODO: remove this when the UnauthenticatedExtensionUIRefresh feature flag is removed.
this.listenForUnauthUiRefreshFlagChanges();
await this.defaultOnInit();
if (this.clientType === ClientType.Desktop) {
@@ -162,6 +168,29 @@ export class LoginComponent implements OnInit, OnDestroy {
this.destroy$.complete();
}
private listenForUnauthUiRefreshFlagChanges() {
this.configService
.getFeatureFlag$(FeatureFlag.UnauthenticatedExtensionUIRefresh)
.pipe(
tap(async (flag) => {
// If the flag is turned OFF, we must force a reload to ensure the correct UI is shown
if (!flag) {
const uniqueQueryParams = {
...this.activatedRoute.queryParams,
// adding a unique timestamp to the query params to force a reload
t: new Date().getTime().toString(), // Adding a unique timestamp as a query parameter
};
await this.router.navigate(["/"], {
queryParams: uniqueQueryParams,
});
}
}),
takeUntil(this.destroy$),
)
.subscribe();
}
submit = async (): Promise<void> => {
if (this.clientType === ClientType.Desktop) {
if (this.loginUiState !== LoginUiState.MASTER_PASSWORD_ENTRY) {

View File

@@ -17,7 +17,6 @@ export enum FeatureFlag {
InlineMenuFieldQualification = "inline-menu-field-qualification",
MemberAccessReport = "ac-2059-member-access-report",
TwoFactorComponentRefactor = "two-factor-component-refactor",
EnableTimeThreshold = "PM-5864-dollar-threshold",
InlineMenuPositioningImprovements = "inline-menu-positioning-improvements",
ProviderClientVaultPrivacyBanner = "ac-2833-provider-client-vault-privacy-banner",
VaultBulkManagementAction = "vault-bulk-management-action",
@@ -63,7 +62,6 @@ export const DefaultFeatureFlagValue = {
[FeatureFlag.InlineMenuFieldQualification]: FALSE,
[FeatureFlag.MemberAccessReport]: FALSE,
[FeatureFlag.TwoFactorComponentRefactor]: FALSE,
[FeatureFlag.EnableTimeThreshold]: FALSE,
[FeatureFlag.InlineMenuPositioningImprovements]: FALSE,
[FeatureFlag.ProviderClientVaultPrivacyBanner]: FALSE,
[FeatureFlag.VaultBulkManagementAction]: FALSE,

View File

@@ -1847,8 +1847,8 @@ export class ApiService implements ApiServiceAbstraction {
const [requestHeaders, requestBody] = await this.buildHeadersAndBody(
authed,
hasResponse,
alterHeaders,
body,
alterHeaders,
);
const requestInit: RequestInit = {

View File

@@ -111,6 +111,7 @@
[value]="totpCodeCopyObj?.totpCodeFormatted || '*** ***'"
aria-readonly="true"
data-testid="login-totp"
class="tw-font-mono"
/>
<div
*ngIf="isPremium$ | async"