Skip to content

Commit

Permalink
Merge branch 'main' into PM-13938-exceptPasswordPermission
Browse files Browse the repository at this point in the history
  • Loading branch information
cd-bitwarden authored Dec 18, 2024
2 parents 6c61320 + ef8e8bf commit c15d193
Show file tree
Hide file tree
Showing 140 changed files with 776 additions and 482 deletions.
2 changes: 1 addition & 1 deletion apps/browser/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@bitwarden/browser",
"version": "2024.12.2",
"version": "2024.12.3",
"scripts": {
"build": "npm run build:chrome",
"build:chrome": "cross-env BROWSER=chrome MANIFEST_VERSION=3 webpack",
Expand Down
2 changes: 1 addition & 1 deletion apps/browser/src/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"manifest_version": 2,
"name": "__MSG_extName__",
"short_name": "__MSG_appName__",
"version": "2024.12.2",
"version": "2024.12.3",
"description": "__MSG_extDesc__",
"default_locale": "en",
"author": "Bitwarden Inc.",
Expand Down
2 changes: 1 addition & 1 deletion apps/browser/src/manifest.v3.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"minimum_chrome_version": "102.0",
"name": "__MSG_extName__",
"short_name": "__MSG_appName__",
"version": "2024.12.2",
"version": "2024.12.3",
"description": "__MSG_extDesc__",
"default_locale": "en",
"author": "Bitwarden Inc.",
Expand Down
4 changes: 4 additions & 0 deletions apps/browser/src/popup/app-routing.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -661,6 +661,10 @@ const routes: Routes = [
* This ensures that in a passkey flow the `/fido2?<queryParams>` URL does not get
* overwritten in the `BrowserRouterService` by the `/lockV2` route. This way, after
* unlocking, the user can be redirected back to the `/fido2?<queryParams>` URL.
*
* Also, this prevents a routing loop when using biometrics to unlock the vault in MV2 (Firefox),
* locking up the browser (https://bitwarden.atlassian.net/browse/PM-16116). This involves the
* `popup-router-cache.service` pushing the `lockV2` route to the history.
*/
doNotSaveUrl: true,
} satisfies ExtensionAnonLayoutWrapperData & RouteDataProperties,
Expand Down
2 changes: 1 addition & 1 deletion apps/desktop/src/main/menu/menu.view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ export class ViewMenu implements IMenubarMenu {
private get passwordHistory(): MenuItemConstructorOptions {
return {
id: "passwordHistory",
label: this.localize("passwordHistory"),
label: this.localize("generatorHistory"),
click: () => this.sendMessage("openPasswordHistory"),
enabled: !this._isLocked,
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@ export class OrganizationLayoutComponent implements OnInit {
showPaymentAndHistory$: Observable<boolean>;
hideNewOrgButton$: Observable<boolean>;
organizationIsUnmanaged$: Observable<boolean>;
isAccessIntelligenceFeatureEnabled = false;
enterpriseOrganization$: Observable<boolean>;

constructor(
Expand Down
14 changes: 14 additions & 0 deletions apps/web/src/app/app.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,20 @@ export class AppComponent implements OnDestroy, OnInit {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
this.router.navigate(["/remove-password"]);
break;
case "syncOrganizationStatusChanged": {
const { organizationId, enabled } = message;
const organizations = await firstValueFrom(this.organizationService.organizations$);
const organization = organizations.find((org) => org.id === organizationId);

if (organization) {
const updatedOrganization = {
...organization,
enabled: enabled,
};
await this.organizationService.upsert(updatedOrganization);
}
break;
}
default:
break;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,6 @@ export class AdjustPaymentDialogComponent {
}
});
await response;
await new Promise((resolve) => setTimeout(resolve, 10000));
this.toastService.showToast({
variant: "success",
title: null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { BehaviorSubject } from "rxjs";

import { I18nPipe } from "@bitwarden/angular/platform/pipes/i18n.pipe";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { BitIconButtonComponent } from "@bitwarden/components/src/icon-button/icon-button.component";
import { IconButtonModule, NavigationModule } from "@bitwarden/components";
import { NavItemComponent } from "@bitwarden/components/src/navigation/nav-item.component";

import { ProductSwitcherItem, ProductSwitcherService } from "../shared/product-switcher.service";
Expand Down Expand Up @@ -45,13 +45,8 @@ describe("NavigationProductSwitcherComponent", () => {
mockProducts$.next({ bento: [], other: [] });

await TestBed.configureTestingModule({
imports: [RouterModule],
declarations: [
NavigationProductSwitcherComponent,
NavItemComponent,
BitIconButtonComponent,
I18nPipe,
],
imports: [RouterModule, NavigationModule, IconButtonModule],
declarations: [NavigationProductSwitcherComponent, I18nPipe],
providers: [
{ provide: ProductSwitcherService, useValue: productSwitcherService },
{
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/app/oss-routing.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -534,7 +534,7 @@ const routes: Routes = [
],
data: {
pageTitle: {
key: "yourAccountIsLocked",
key: "yourVaultIsLockedV2",
},
pageIcon: LockIcon,
showReadonlyHostname: true,
Expand Down
3 changes: 3 additions & 0 deletions apps/web/src/locales/en/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -3882,6 +3882,9 @@
"updateBrowser": {
"message": "Update browser"
},
"generatingRiskInsights": {
"message": "Generating your risk insights..."
},
"updateBrowserDesc": {
"message": "You are using an unsupported web browser. The web vault may not function properly."
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export type ApplicationHealthReportDetail = {
passwordCount: number;
atRiskPasswordCount: number;
memberCount: number;

atRiskMemberCount: number;
memberDetails: MemberDetailsFlat[];
atRiskMemberDetails: MemberDetailsFlat[];
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from "./member-cipher-details-api.service";
export * from "./password-health.service";
export * from "./risk-insights-report.service";
export * from "./risk-insights-data.service";
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { BehaviorSubject } from "rxjs";
import { finalize } from "rxjs/operators";

import { ApplicationHealthReportDetail } from "../models/password-health";

import { RiskInsightsReportService } from "./risk-insights-report.service";

export class RiskInsightsDataService {
private applicationsSubject = new BehaviorSubject<ApplicationHealthReportDetail[] | null>(null);

applications$ = this.applicationsSubject.asObservable();

private isLoadingSubject = new BehaviorSubject<boolean>(false);
isLoading$ = this.isLoadingSubject.asObservable();

private isRefreshingSubject = new BehaviorSubject<boolean>(false);
isRefreshing$ = this.isRefreshingSubject.asObservable();

private errorSubject = new BehaviorSubject<string | null>(null);
error$ = this.errorSubject.asObservable();

private dataLastUpdatedSubject = new BehaviorSubject<Date | null>(null);
dataLastUpdated$ = this.dataLastUpdatedSubject.asObservable();

constructor(private reportService: RiskInsightsReportService) {}

/**
* Fetches the applications report and updates the applicationsSubject.
* @param organizationId The ID of the organization.
*/
fetchApplicationsReport(organizationId: string, isRefresh?: boolean): void {
if (isRefresh) {
this.isRefreshingSubject.next(true);
} else {
this.isLoadingSubject.next(true);
}
this.reportService
.generateApplicationsReport$(organizationId)
.pipe(
finalize(() => {
this.isLoadingSubject.next(false);
this.isRefreshingSubject.next(false);
this.dataLastUpdatedSubject.next(new Date());
}),
)
.subscribe({
next: (reports: ApplicationHealthReportDetail[]) => {
this.applicationsSubject.next(reports);
this.errorSubject.next(null);
},
error: () => {
this.applicationsSubject.next([]);
},
});
}

refreshApplicationsReport(organizationId: string): void {
this.fetchApplicationsReport(organizationId, true);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { TestBed } from "@angular/core/testing";
import { mock } from "jest-mock-extended";
import { firstValueFrom } from "rxjs";
import { ZXCVBNResult } from "zxcvbn";

import { AuditService } from "@bitwarden/common/abstractions/audit.service";
import { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/password-strength";
Expand All @@ -12,42 +13,31 @@ import { RiskInsightsReportService } from "./risk-insights-report.service";

describe("RiskInsightsReportService", () => {
let service: RiskInsightsReportService;
const pwdStrengthService = mock<PasswordStrengthServiceAbstraction>();
const auditService = mock<AuditService>();
const cipherService = mock<CipherService>();
const memberCipherDetailsService = mock<MemberCipherDetailsApiService>();

beforeEach(() => {
TestBed.configureTestingModule({
providers: [
RiskInsightsReportService,
{
provide: PasswordStrengthServiceAbstraction,
useValue: {
getPasswordStrength: (password: string) => {
const score = password.length < 4 ? 1 : 4;
return { score };
},
},
},
{
provide: AuditService,
useValue: {
passwordLeaked: (password: string) => Promise.resolve(password === "123" ? 100 : 0),
},
},
{
provide: CipherService,
useValue: {
getAllFromApiForOrganization: jest.fn().mockResolvedValue(mockCiphers),
},
},
{
provide: MemberCipherDetailsApiService,
useValue: {
getMemberCipherDetails: jest.fn().mockResolvedValue(mockMemberCipherDetails),
},
},
],
pwdStrengthService.getPasswordStrength.mockImplementation((password: string) => {
const score = password.length < 4 ? 1 : 4;
return { score } as ZXCVBNResult;
});

service = TestBed.inject(RiskInsightsReportService);
auditService.passwordLeaked.mockImplementation((password: string) =>
Promise.resolve(password === "123" ? 100 : 0),
);

cipherService.getAllFromApiForOrganization.mockResolvedValue(mockCiphers);

memberCipherDetailsService.getMemberCipherDetails.mockResolvedValue(mockMemberCipherDetails);

service = new RiskInsightsReportService(
pwdStrengthService,
auditService,
cipherService,
memberCipherDetailsService,
);
});

it("should generate the raw data report correctly", async () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
// FIXME: Update this file to be type safe and remove this and next line
// FIXME: Update this file to be type safe
// @ts-strict-ignore

import { Injectable } from "@angular/core";
import { concatMap, first, from, map, Observable, zip } from "rxjs";

import { AuditService } from "@bitwarden/common/abstractions/audit.service";
Expand All @@ -24,7 +22,6 @@ import {

import { MemberCipherDetailsApiService } from "./member-cipher-details-api.service";

@Injectable()
export class RiskInsightsReportService {
constructor(
private passwordStrengthService: PasswordStrengthServiceAbstraction,
Expand Down Expand Up @@ -290,6 +287,7 @@ export class RiskInsightsReportService {
: newUriDetail.cipherMembers,
atRiskMemberDetails: existingUriDetail ? existingUriDetail.atRiskMemberDetails : [],
atRiskPasswordCount: existingUriDetail ? existingUriDetail.atRiskPasswordCount : 0,
atRiskMemberCount: existingUriDetail ? existingUriDetail.atRiskMemberDetails.length : 0,
} as ApplicationHealthReportDetail;

if (isAtRisk) {
Expand Down
2 changes: 2 additions & 0 deletions bitwarden_license/bit-web/src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { MaximumVaultTimeoutPolicyComponent } from "./admin-console/policies/max
import { AppRoutingModule } from "./app-routing.module";
import { AppComponent } from "./app.component";
import { FreeFamiliesSponsorshipPolicyComponent } from "./billing/policies/free-families-sponsorship.component";
import { AccessIntelligenceModule } from "./tools/access-intelligence/access-intelligence.module";

/**
* This is the AppModule for the commercial version of Bitwarden.
Expand All @@ -41,6 +42,7 @@ import { FreeFamiliesSponsorshipPolicyComponent } from "./billing/policies/free-
AppRoutingModule,
OssRoutingModule,
OrganizationsModule, // Must be after OssRoutingModule for competing routes to resolve properly
AccessIntelligenceModule,
RouterModule,
WildcardRoutingModule, // Needs to be last to catch all non-existing routes
],
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,38 @@
import { NgModule } from "@angular/core";

import {
MemberCipherDetailsApiService,
RiskInsightsDataService,
RiskInsightsReportService,
} from "@bitwarden/bit-common/tools/reports/risk-insights/services";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { AuditService } from "@bitwarden/common/abstractions/audit.service";
import { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/password-strength/password-strength.service.abstraction";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";

import { AccessIntelligenceRoutingModule } from "./access-intelligence-routing.module";
import { RiskInsightsComponent } from "./risk-insights.component";

@NgModule({
imports: [RiskInsightsComponent, AccessIntelligenceRoutingModule],
providers: [
{
provide: MemberCipherDetailsApiService,
deps: [ApiService],
},
{
provide: RiskInsightsReportService,
deps: [
PasswordStrengthServiceAbstraction,
AuditService,
CipherService,
MemberCipherDetailsApiService,
],
},
{
provide: RiskInsightsDataService,
deps: [RiskInsightsReportService],
},
],
})
export class AccessIntelligenceModule {}
Loading

0 comments on commit c15d193

Please sign in to comment.