1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-11 05:53:42 +00:00

Merge branch 'main' into km/fix-autoprompt

This commit is contained in:
Bernd Schoolmann
2025-01-14 16:26:24 +01:00
committed by GitHub
112 changed files with 835 additions and 293 deletions

View File

@@ -66,10 +66,9 @@ jobs:
ref: ${{ github.event.pull_request.head.sha }}
- name: Scan with SonarCloud
uses: sonarsource/sonarcloud-github-action@02ef91109b2d589e757aefcfb2854c2783fd7b19 # v4.0.0
uses: sonarsource/sonarqube-scan-action@bfd4e558cda28cda6b5defafb9232d191be8c203 # v4.2.1
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
args: >
-Dsonar.organization=${{ github.repository_owner }}

View File

@@ -1,6 +1,6 @@
{
"name": "@bitwarden/browser",
"version": "2025.1.0",
"version": "2025.1.1",
"scripts": {
"build": "npm run build:chrome",
"build:chrome": "cross-env BROWSER=chrome MANIFEST_VERSION=3 webpack",
@@ -30,6 +30,7 @@
"dist:safari:mv3": "cross-env MANIFEST_VERSION=3 npm run dist:safari",
"test": "jest",
"test:watch": "jest --watch",
"test:watch:all": "jest --watchAll"
"test:watch:all": "jest --watchAll",
"test:clearCache": "jest --clear-cache"
}
}

View File

@@ -2339,11 +2339,11 @@
"blockedDomainsDesc": {
"message": "Autofill and other related features will not be offered for these websites. You must refresh the page for changes to take effect."
},
"autofillBlockedNotice": {
"message": "Autofill is blocked for this website. Review or change this in settings."
"autofillBlockedNoticeV2": {
"message": "Autofill is blocked for this website."
},
"autofillBlockedTooltip": {
"message": "Autofill is blocked on this website. Review in settings."
"autofillBlockedNoticeGuidance": {
"message": "Change this in settings"
},
"websiteItemLabel": {
"message": "Website $number$ (URI)",
@@ -4007,8 +4007,8 @@
"passkeyRemoved": {
"message": "Passkey removed"
},
"autofillSuggestions": {
"message": "Autofill suggestions"
"itemSuggestions": {
"message": "Suggested items"
},
"autofillSuggestionsTip": {
"message": "Save a login item for this site to autofill"

View File

@@ -57,6 +57,17 @@ export type InlineMenuElementPosition = {
height: number;
};
export type FieldRect = {
bottom: number;
height: number;
left: number;
right: number;
top: number;
width: number;
x: number;
y: number;
};
export type InlineMenuPosition = {
button?: InlineMenuElementPosition;
list?: InlineMenuElementPosition;
@@ -134,6 +145,7 @@ export type OverlayBackgroundExtensionMessage = {
isFieldCurrentlyFilling?: boolean;
subFrameData?: SubFrameOffsetData;
focusedFieldData?: FocusedFieldData;
allFieldsRect?: any;
isOpeningFullInlineMenu?: boolean;
styles?: Partial<CSSStyleDeclaration>;
data?: LockedVaultPendingNotificationsData;

View File

@@ -2913,6 +2913,124 @@ describe("OverlayBackground", () => {
);
});
});
describe("handles menu position when input is focused", () => {
it("sets button and menu width and position when non-multi-input totp field is focused", async () => {
const subframe = {
top: 0,
left: 0,
url: "",
frameId: 0,
};
overlayBackground["focusedFieldData"] = createFocusedFieldDataMock({
focusedFieldRects: {
width: 49.328125,
height: 64,
top: 302.171875,
left: 1270.8125,
},
});
const buttonPostion = overlayBackground["getInlineMenuButtonPosition"](subframe);
const menuPostion = overlayBackground["getInlineMenuListPosition"](subframe);
expect(menuPostion).toEqual({
width: "49px",
top: "366px",
left: "1271px",
});
expect(buttonPostion).toEqual({
width: "34px",
height: "34px",
top: "317px",
left: "1271px",
});
});
it("sets button and menu width and position when multi-input totp field is focused", async () => {
const subframe = {
top: 0,
left: 0,
url: "",
frameId: 0,
};
const totpFields = [
createAutofillFieldMock({ autoCompleteType: "one-time-code", opid: "__0" }),
createAutofillFieldMock({ autoCompleteType: "one-time-code", opid: "__1" }),
createAutofillFieldMock({ autoCompleteType: "one-time-code", opid: "__2" }),
];
const allFieldData = [
createAutofillFieldMock({
autoCompleteType: "one-time-code",
opid: "__0",
rect: {
x: 1041.5,
y: 302.171875,
width: 49.328125,
height: 64,
top: 302.171875,
right: 1090.828125,
bottom: 366.171875,
left: 1041.5,
},
}),
createAutofillFieldMock({
autoCompleteType: "one-time-code",
opid: "__1",
rect: {
x: 1098.828125,
y: 302.171875,
width: 49.328125,
height: 64,
top: 302.171875,
right: 1148.15625,
bottom: 366.171875,
left: 1098.828125,
},
}),
createAutofillFieldMock({
autoCompleteType: "one-time-code",
opid: "__2",
rect: {
x: 1156.15625,
y: 302.171875,
width: 249.328125,
height: 64,
top: 302.171875,
right: 2205.484375,
bottom: 366.171875,
left: 2156.15625,
},
}),
];
overlayBackground["focusedFieldData"] = createFocusedFieldDataMock({
focusedFieldRects: {
width: 49.328125,
height: 64,
top: 302.171875,
left: 1270.8125,
},
});
overlayBackground["allFieldData"] = allFieldData;
jest.spyOn(overlayBackground as any, "isTotpFieldForCurrentField").mockReturnValue(true);
jest.spyOn(overlayBackground as any, "getTotpFields").mockReturnValue(totpFields);
const buttonPostion = overlayBackground["getInlineMenuButtonPosition"](subframe);
const menuPostion = overlayBackground["getInlineMenuListPosition"](subframe);
expect(menuPostion).toEqual({
width: "1164px",
top: "366px",
left: "1042px",
});
expect(buttonPostion).toEqual({
width: "34px",
height: "34px",
top: "292px",
left: "2187px",
});
});
});
describe("triggerDelayedAutofillInlineMenuClosure message handler", () => {
it("skips triggering the delayed closure of the inline menu if a field is currently focused", async () => {

View File

@@ -70,6 +70,7 @@ import {
generateDomainMatchPatterns,
generateRandomChars,
isInvalidResponseStatusCode,
rectHasSize,
specialCharacterToKeyMap,
} from "../utils";
@@ -130,6 +131,7 @@ export class OverlayBackground implements OverlayBackgroundInterface {
private currentInlineMenuCiphersCount: number = 0;
private currentAddNewItemData: CurrentAddNewItemData;
private focusedFieldData: FocusedFieldData;
private allFieldData: AutofillField[];
private isFieldCurrentlyFocused: boolean = false;
private isFieldCurrentlyFilling: boolean = false;
private isInlineMenuButtonVisible: boolean = false;
@@ -1367,6 +1369,71 @@ export class OverlayBackground implements OverlayBackgroundInterface {
this.isInlineMenuListVisible = false;
}
/**
* Get all the totp fields for the tab and frame of the currently focused field
*/
private getTotpFields(): AutofillField[] {
const currentTabId = this.focusedFieldData?.tabId;
const currentFrameId = this.focusedFieldData?.frameId;
const pageDetailsMap = this.pageDetailsForTab[currentTabId];
const pageDetails = pageDetailsMap?.get(currentFrameId);
const fields = pageDetails.details.fields;
const totpFields = fields.filter((f) =>
this.inlineMenuFieldQualificationService.isTotpField(f),
);
return totpFields;
}
/**
* calculates the postion and width for multi-input totp field inline menu
* @param totpFieldArray - the totp fields used to evaluate the position of the menu
*/
private calculateTotpMultiInputMenuBounds(totpFieldArray: AutofillField[]) {
// Filter the fields based on the provided totpfields
const filteredObjects = this.allFieldData.filter((obj) =>
totpFieldArray.some((o) => o.opid === obj.opid),
);
// Return null if no matching objects are found
if (filteredObjects.length === 0) {
return null;
}
// Calculate the smallest left and largest right values to determine width
const left = Math.min(
...filteredObjects.filter((obj) => rectHasSize(obj.rect)).map((obj) => obj.rect.left),
);
const largestRight = Math.max(
...filteredObjects.filter((obj) => rectHasSize(obj.rect)).map((obj) => obj.rect.right),
);
const width = largestRight - left;
return { left, width };
}
/**
* calculates the postion for multi-input totp field inline button
* @param totpFieldArray - the totp fields used to evaluate the position of the menu
*/
private calculateTotpMultiInputButtonBounds(totpFieldArray: AutofillField[]) {
const filteredObjects = this.allFieldData.filter((obj) =>
totpFieldArray.some((o) => o.opid === obj.opid),
);
if (filteredObjects.length === 0) {
return null;
}
const maxRight = Math.max(...filteredObjects.map((obj) => obj.rect.right));
const maxObject = filteredObjects.find((obj) => obj.rect.right === maxRight);
const top = maxObject.rect.top - maxObject.rect.height * 0.39;
const left = maxRight - maxObject.rect.height * 0.3;
return { left, top };
}
/**
* Updates the position of either the inline menu list or button. The position
* is based on the focused field's position and dimensions.
@@ -1472,8 +1539,17 @@ export class OverlayBackground implements OverlayBackgroundInterface {
const subFrameTopOffset = subFrameOffsets?.top || 0;
const subFrameLeftOffset = subFrameOffsets?.left || 0;
const { top, left, width, height } = this.focusedFieldData.focusedFieldRects;
const { width, height } = this.focusedFieldData.focusedFieldRects;
let { top, left } = this.focusedFieldData.focusedFieldRects;
const { paddingRight, paddingLeft } = this.focusedFieldData.focusedFieldStyles;
if (this.isTotpFieldForCurrentField()) {
const totpFields = this.getTotpFields();
if (totpFields.length > 1) {
({ left, top } = this.calculateTotpMultiInputButtonBounds(totpFields));
}
}
let elementOffset = height * 0.37;
if (height >= 35) {
elementOffset = height >= 50 ? height * 0.47 : height * 0.42;
@@ -1512,7 +1588,16 @@ export class OverlayBackground implements OverlayBackgroundInterface {
const subFrameTopOffset = subFrameOffsets?.top || 0;
const subFrameLeftOffset = subFrameOffsets?.left || 0;
const { top, left, width, height } = this.focusedFieldData.focusedFieldRects;
const { top, height } = this.focusedFieldData.focusedFieldRects;
let { left, width } = this.focusedFieldData.focusedFieldRects;
if (this.isTotpFieldForCurrentField()) {
const totpFields = this.getTotpFields();
if (totpFields.length > 1) {
({ left, width } = this.calculateTotpMultiInputMenuBounds(totpFields));
}
}
this.inlineMenuPosition.list = {
top: Math.round(top + height + subFrameTopOffset),
@@ -1535,7 +1620,7 @@ export class OverlayBackground implements OverlayBackgroundInterface {
* @param sender - The sender of the extension message
*/
private setFocusedFieldData(
{ focusedFieldData }: OverlayBackgroundExtensionMessage,
{ focusedFieldData, allFieldsRect }: OverlayBackgroundExtensionMessage,
sender: chrome.runtime.MessageSender,
) {
if (
@@ -1552,6 +1637,7 @@ export class OverlayBackground implements OverlayBackgroundInterface {
const previousFocusedFieldData = this.focusedFieldData;
this.focusedFieldData = { ...focusedFieldData, tabId: sender.tab.id, frameId: sender.frameId };
this.allFieldData = allFieldsRect;
this.isFieldCurrentlyFocused = true;
if (this.shouldUpdatePasswordGeneratorMenuOnFieldFocus()) {

View File

@@ -1,3 +1,4 @@
import { FieldRect } from "../background/abstractions/overlay.background";
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import { AutofillFieldQualifierType } from "../enums/autofill-field.enums";
@@ -124,4 +125,9 @@ export default class AutofillField {
fieldQualifier?: AutofillFieldQualifierType;
accountCreationFieldType?: InlineMenuAccountCreationFieldTypes;
/**
* used for totp multiline calculations
*/
fieldRect?: FieldRect;
}

View File

@@ -957,8 +957,17 @@ export class AutofillOverlayContentService implements AutofillOverlayContentServ
accountCreationFieldType: autofillFieldData?.accountCreationFieldType,
};
const allFields = this.formFieldElements;
const allFieldsRect = [];
for (const key of allFields.keys()) {
const rect = await this.getMostRecentlyFocusedFieldRects(key);
allFieldsRect.push({ ...allFields.get(key), rect }); // Add the combined result to the array
}
await this.sendExtensionMessage("updateFocusedFieldData", {
focusedFieldData: this.focusedFieldData,
allFieldsRect,
});
}

View File

@@ -1,5 +1,6 @@
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import { FieldRect } from "../background/abstractions/overlay.background";
import { AutofillPort } from "../enums/autofill-port.enum";
import { FillableFormFieldElement, FormElementWithAttribute, FormFieldElement } from "../types";
@@ -545,6 +546,17 @@ export const specialCharacterToKeyMap: Record<string, string> = {
"/": "forwardSlashCharacterDescriptor",
};
/**
* Determines if the current rect values are not all 0.
*/
export function rectHasSize(rect: FieldRect): boolean {
if (rect.right > 0 && rect.left > 0 && rect.top > 0 && rect.bottom > 0) {
return true;
}
return false;
}
/**
* Checks if all the values corresponding to the specified keys in an object are null.
* If no keys are specified, checks all keys in the object.

View File

@@ -2,7 +2,7 @@
"manifest_version": 2,
"name": "__MSG_extName__",
"short_name": "__MSG_appName__",
"version": "2025.1.0",
"version": "2025.1.1",
"description": "__MSG_extDesc__",
"default_locale": "en",
"author": "Bitwarden Inc.",

View File

@@ -3,7 +3,7 @@
"minimum_chrome_version": "102.0",
"name": "__MSG_extName__",
"short_name": "__MSG_appName__",
"version": "2025.1.0",
"version": "2025.1.1",
"description": "__MSG_extDesc__",
"default_locale": "en",
"author": "Bitwarden Inc.",

View File

@@ -1,7 +1,7 @@
<app-vault-list-items-container
*ngIf="autofillCiphers$ | async as ciphers"
[ciphers]="ciphers"
[title]="'autofillSuggestions' | i18n"
[title]="'itemSuggestions' | i18n"
[showRefresh]="showRefresh"
(onRefresh)="refreshCurrentTab()"
[description]="(showEmptyAutofillTip$ | async) ? ('autofillSuggestionsTip' | i18n) : null"

View File

@@ -0,0 +1,10 @@
<bit-banner
*ngIf="showCurrentTabIsBlockedBanner$ | async"
bannerType="info"
(onClose)="handleCurrentTabIsBlockedBannerDismiss()"
>
{{ "autofillBlockedNoticeV2" | i18n }}
<a bitLink linkType="secondary" [routerLink]="blockedURISettingsRoute">
{{ "autofillBlockedNoticeGuidance" | i18n }}
</a>
</bit-banner>

View File

@@ -0,0 +1,53 @@
import { CommonModule } from "@angular/common";
import { Component, OnInit } from "@angular/core";
import { RouterModule } from "@angular/router";
import { Observable } from "rxjs";
import { JslibModule } from "@bitwarden/angular/jslib.module";
import {
BannerModule,
IconButtonModule,
LinkModule,
TypographyModule,
} from "@bitwarden/components";
import { VaultPopupAutofillService } from "../../../services/vault-popup-autofill.service";
const blockedURISettingsRoute = "/blocked-domains";
@Component({
standalone: true,
imports: [
BannerModule,
CommonModule,
IconButtonModule,
JslibModule,
LinkModule,
RouterModule,
TypographyModule,
],
selector: "blocked-injection-banner",
templateUrl: "blocked-injection-banner.component.html",
})
export class BlockedInjectionBanner implements OnInit {
/**
* Flag indicating that the banner should be shown
*/
protected showCurrentTabIsBlockedBanner$: Observable<boolean> =
this.vaultPopupAutofillService.showCurrentTabIsBlockedBanner$;
/**
* Hostname for current tab
*/
protected currentTabHostname?: string;
blockedURISettingsRoute: string = blockedURISettingsRoute;
constructor(private vaultPopupAutofillService: VaultPopupAutofillService) {}
async ngOnInit() {}
async handleCurrentTabIsBlockedBannerDismiss() {
await this.vaultPopupAutofillService.dismissCurrentTabIsBlockedBanner();
}
}

View File

@@ -80,11 +80,9 @@
<button
bit-item-content
type="button"
(click)="primaryActionAutofill ? doAutofill(cipher) : onViewCipher(cipher)"
(click)="primaryActionOnSelect(cipher)"
(dblclick)="launchCipher(cipher)"
[appA11yTitle]="
(primaryActionAutofill ? 'autofillTitle' : 'viewItemTitle') | i18n: cipher.name
"
[appA11yTitle]="cipherItemTitleKey | async | i18n: cipher.name"
class="{{ itemHeightClass }}"
>
<div slot="start" class="tw-justify-start tw-w-7 tw-flex">
@@ -106,7 +104,7 @@
<span slot="secondary">{{ cipher.subTitle }}</span>
</button>
<ng-container slot="end">
<bit-item-action *ngIf="showAutofillButton && !primaryActionAutofill">
<bit-item-action *ngIf="!(hideAutofillButton$ | async)">
<button
type="button"
bitBadge
@@ -131,7 +129,7 @@
<app-item-copy-actions [cipher]="cipher"></app-item-copy-actions>
<app-item-more-options
[cipher]="cipher"
[hideAutofillOptions]="showAutofillButton"
[hideAutofillOptions]="hideAutofillOptions$ | async"
[showViewOption]="primaryActionAutofill"
></app-item-more-options>
</ng-container>

View File

@@ -15,8 +15,8 @@ import {
signal,
ViewChild,
} from "@angular/core";
import { Router, RouterLink } from "@angular/router";
import { map } from "rxjs";
import { Router } from "@angular/router";
import { firstValueFrom, Observable, map } from "rxjs";
import { JslibModule } from "@bitwarden/angular/jslib.module";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
@@ -62,7 +62,6 @@ import { ItemMoreOptionsComponent } from "../item-more-options/item-more-options
TypographyModule,
JslibModule,
SectionHeaderComponent,
RouterLink,
ItemCopyActionsComponent,
ItemMoreOptionsComponent,
OrgIconDirective,
@@ -151,12 +150,41 @@ export class VaultListItemsContainerComponent implements OnInit, AfterViewInit {
@Output()
onRefresh = new EventEmitter<void>();
/**
* Flag indicating that the current tab location is blocked
*/
currentURIIsBlocked$: Observable<boolean> =
this.vaultPopupAutofillService.currentTabIsOnBlocklist$;
/**
* Resolved i18n key to use for suggested cipher items
*/
cipherItemTitleKey = this.currentURIIsBlocked$.pipe(
map((uriIsBlocked) =>
this.primaryActionAutofill && !uriIsBlocked ? "autofillTitle" : "viewItemTitle",
),
);
/**
* Option to show the autofill button for each item.
*/
@Input({ transform: booleanAttribute })
showAutofillButton: boolean;
/**
* Flag indicating whether the suggested cipher item autofill button should be shown or not
*/
hideAutofillButton$ = this.currentURIIsBlocked$.pipe(
map((uriIsBlocked) => !this.showAutofillButton || uriIsBlocked || this.primaryActionAutofill),
);
/**
* Flag indicating whether the cipher item autofill options should be shown or not
*/
hideAutofillOptions$: Observable<boolean> = this.currentURIIsBlocked$.pipe(
map((uriIsBlocked) => uriIsBlocked || this.showAutofillButton),
);
/**
* Option to perform autofill operation as the primary action for autofill suggestions.
*/
@@ -216,6 +244,14 @@ export class VaultListItemsContainerComponent implements OnInit, AfterViewInit {
}
}
async primaryActionOnSelect(cipher: CipherView) {
const isBlocked = await firstValueFrom(this.currentURIIsBlocked$);
return this.primaryActionAutofill && !isBlocked
? this.doAutofill(cipher)
: this.onViewCipher(cipher);
}
/**
* Launches the login cipher in a new browser tab.
*/

View File

@@ -22,6 +22,11 @@
</bit-no-items>
</div>
<blocked-injection-banner
*ngIf="vaultState !== VaultStateEnum.Empty"
slot="full-width-notice"
></blocked-injection-banner>
<!-- Show search & filters outside of the scroll area of the page -->
<ng-container
slot="above-scroll-area"

View File

@@ -21,6 +21,7 @@ import { VaultPopupItemsService } from "../../services/vault-popup-items.service
import { VaultPopupListFiltersService } from "../../services/vault-popup-list-filters.service";
import { VaultUiOnboardingService } from "../../services/vault-ui-onboarding.service";
import { BlockedInjectionBanner } from "./blocked-injection-banner/blocked-injection-banner.component";
import {
NewItemDropdownV2Component,
NewItemInitialValues,
@@ -40,6 +41,7 @@ enum VaultState {
templateUrl: "vault-v2.component.html",
standalone: true,
imports: [
BlockedInjectionBanner,
PopupPageComponent,
PopupHeaderComponent,
PopOutComponent,

View File

@@ -4,6 +4,7 @@ import { mock } from "jest-mock-extended";
import { BehaviorSubject, of } from "rxjs";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { DomainSettingsService } from "@bitwarden/common/autofill/services/domain-settings.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
@@ -44,6 +45,7 @@ describe("VaultPopupAutofillService", () => {
// Create mocks for VaultPopupAutofillService
const mockAutofillService = mock<AutofillService>();
const mockDomainSettingsService = mock<DomainSettingsService>();
const mockI18nService = mock<I18nService>();
const mockToastService = mock<ToastService>();
const mockPlatformUtilsService = mock<PlatformUtilsService>();
@@ -71,6 +73,7 @@ describe("VaultPopupAutofillService", () => {
testBed = TestBed.configureTestingModule({
providers: [
{ provide: AutofillService, useValue: mockAutofillService },
{ provide: DomainSettingsService, useValue: mockDomainSettingsService },
{ provide: I18nService, useValue: mockI18nService },
{ provide: ToastService, useValue: mockToastService },
{ provide: PlatformUtilsService, useValue: mockPlatformUtilsService },

View File

@@ -15,6 +15,7 @@ import {
} from "rxjs";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { DomainSettingsService } from "@bitwarden/common/autofill/services/domain-settings.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
@@ -67,6 +68,72 @@ export class VaultPopupAutofillService {
shareReplay({ refCount: false, bufferSize: 1 }),
);
currentTabIsOnBlocklist$: Observable<boolean> = combineLatest([
this.domainSettingsService.blockedInteractionsUris$,
this.currentAutofillTab$,
]).pipe(
map(([blockedInteractionsUris, currentTab]) => {
if (blockedInteractionsUris && currentTab?.url?.length) {
const tabURL = new URL(currentTab.url);
const tabIsBlocked = Object.keys(blockedInteractionsUris).includes(tabURL.hostname);
if (tabIsBlocked) {
return true;
}
}
return false;
}),
shareReplay({ refCount: false, bufferSize: 1 }),
);
showCurrentTabIsBlockedBanner$: Observable<boolean> = combineLatest([
this.domainSettingsService.blockedInteractionsUris$,
this.currentAutofillTab$,
]).pipe(
map(([blockedInteractionsUris, currentTab]) => {
if (blockedInteractionsUris && currentTab?.url?.length) {
const tabURL = new URL(currentTab.url);
const tabIsBlocked = Object.keys(blockedInteractionsUris).includes(tabURL.hostname);
const showScriptInjectionIsBlockedBanner =
tabIsBlocked && !blockedInteractionsUris[tabURL.hostname]?.bannerIsDismissed;
return showScriptInjectionIsBlockedBanner;
}
return false;
}),
shareReplay({ refCount: false, bufferSize: 1 }),
);
async dismissCurrentTabIsBlockedBanner() {
try {
const currentTab = await firstValueFrom(this.currentAutofillTab$);
const currentTabURL = currentTab?.url.length && new URL(currentTab.url);
const currentTabHostname = currentTabURL && currentTabURL.hostname;
if (!currentTabHostname) {
return;
}
const blockedURIs = await firstValueFrom(this.domainSettingsService.blockedInteractionsUris$);
const tabIsBlocked = Object.keys(blockedURIs).includes(currentTabHostname);
if (tabIsBlocked) {
void this.domainSettingsService.setBlockedInteractionsUris({
...blockedURIs,
[currentTabHostname as string]: { bannerIsDismissed: true },
});
}
} catch (e) {
throw new Error(
"There was a problem dismissing the blocked interaction URI notification banner",
);
}
}
/**
* Observable that indicates whether autofill is allowed in the current context.
* Autofill is allowed when there is a current tab and the popup is not in a popout window.
@@ -125,6 +192,7 @@ export class VaultPopupAutofillService {
constructor(
private autofillService: AutofillService,
private domainSettingsService: DomainSettingsService,
private i18nService: I18nService,
private toastService: ToastService,
private platformUtilService: PlatformUtilsService,

View File

@@ -1,7 +1,7 @@
{
"name": "@bitwarden/cli",
"description": "A secure and free password manager for all of your devices.",
"version": "2025.1.0",
"version": "2025.1.1",
"keywords": [
"bitwarden",
"password",

View File

@@ -20,7 +20,7 @@
"**/node_modules/@bitwarden/desktop-napi/index.js",
"**/node_modules/@bitwarden/desktop-napi/desktop_napi.${platform}-${arch}*.node"
],
"electronVersion": "33.2.1",
"electronVersion": "33.3.1",
"generateUpdatesFilesForAllChannels": true,
"publish": {
"provider": "generic",

View File

@@ -1,7 +1,7 @@
{
"name": "@bitwarden/desktop",
"description": "A secure and free password manager for all of your devices.",
"version": "2025.1.0",
"version": "2025.1.1",
"keywords": [
"bitwarden",
"password",

View File

@@ -1,12 +1,12 @@
{
"name": "@bitwarden/desktop",
"version": "2025.1.0",
"version": "2025.1.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@bitwarden/desktop",
"version": "2025.1.0",
"version": "2025.1.1",
"license": "GPL-3.0",
"dependencies": {
"@bitwarden/desktop-napi": "file:../desktop_native/napi"

View File

@@ -2,7 +2,7 @@
"name": "@bitwarden/desktop",
"productName": "Bitwarden",
"description": "A secure and free password manager for all of your devices.",
"version": "2025.1.0",
"version": "2025.1.1",
"author": "Bitwarden Inc. <hello@bitwarden.com> (https://bitwarden.com)",
"homepage": "https://bitwarden.com",
"license": "GPL-3.0",

View File

@@ -83,6 +83,15 @@ export class SshAgentService implements OnDestroy {
this.messageListener
.messages$(new CommandDefinition("sshagent.signrequest"))
.pipe(
withLatestFrom(this.desktopSettingsService.sshAgentEnabled$),
concatMap(async ([message, enabled]) => {
if (!enabled) {
await ipc.platform.sshAgent.signRequestResponse(message.requestId as number, false);
}
return { message, enabled };
}),
filter(({ enabled }) => enabled),
map(({ message }) => message),
withLatestFrom(this.authService.activeAccountStatus$),
// This switchMap handles unlocking the vault if it is locked:
// - If the vault is locked, we will wait for it to be unlocked.

View File

@@ -1,6 +1,6 @@
{
"name": "@bitwarden/web-vault",
"version": "2025.1.0",
"version": "2025.1.1",
"scripts": {
"build:oss": "cross-env NODE_OPTIONS=\"--max-old-space-size=8192\" webpack",
"build:bit": "cross-env NODE_OPTIONS=\"--max-old-space-size=8192\" webpack -c ../../bitwarden_license/bit-web/webpack.config.js",

View File

@@ -23,15 +23,19 @@
<bit-tab [label]="'role' | i18n">
<ng-container *ngIf="!editMode">
<p bitTypography="body1">{{ "inviteUserDesc" | i18n }}</p>
<bit-form-field *ngIf="remainingSeats$ | async as remainingSeats">
<bit-form-field *ngIf="{ seats: remainingSeats$ | async } as remaining">
<bit-label>{{ "email" | i18n }}</bit-label>
<input id="emails" type="text" appAutoFocus bitInput formControlName="emails" />
<bit-hint *ngIf="remainingSeats > 1; else singleSeat">{{
"inviteMultipleEmailDesc" | i18n: remainingSeats
<bit-hint *ngIf="remaining.seats > 1">{{
"inviteMultipleEmailDesc" | i18n: remaining.seats
}}</bit-hint>
<ng-template #singleSeat>
<bit-hint>{{ "inviteSingleEmailDesc" | i18n: remainingSeats }}</bit-hint>
</ng-template>
<bit-hint *ngIf="remaining.seats === 1">{{
"inviteSingleEmailDesc" | i18n
}}</bit-hint>
<bit-hint *ngIf="remaining.seats === 0">{{ "inviteZeroEmailDesc" | i18n }}</bit-hint>
</bit-form-field>
</ng-container>
<bit-radio-group formControlName="type">

View File

@@ -48,17 +48,31 @@
<i [class]="getDeviceIcon(row.type)" class="bwi-lg" aria-hidden="true"></i>
</div>
<div>
{{ row.displayName }}
<span *ngIf="row.trusted" class="tw-text-sm tw-text-muted tw-block">
{{ "trusted" | i18n }}
</span>
<ng-container *ngIf="row.hasPendingAuthRequest">
<a bitLink href="#" appStopClick (click)="managePendingAuthRequest(row)">
{{ row.displayName }}
</a>
<span class="tw-text-sm tw-text-muted tw-block">
{{ "needsApproval" | i18n }}
</span>
</ng-container>
<ng-container *ngIf="!row.hasPendingAuthRequest">
{{ row.displayName }}
<span
*ngIf="row.trusted && !row.hasPendingAuthRequest"
class="tw-text-sm tw-text-muted tw-block"
>
{{ "trusted" | i18n }}
</span>
</ng-container>
</div>
</td>
<td bitCell>
<span *ngIf="isCurrentDevice(row)" bitBadge variant="primary">{{
"currentSession" | i18n
}}</span>
<span *ngIf="hasPendingAuthRequest(row)" bitBadge variant="warning">{{
<span *ngIf="row.hasPendingAuthRequest" bitBadge variant="warning">{{
"requestPending" | i18n
}}</span>
</td>

View File

@@ -1,10 +1,14 @@
import { CommonModule } from "@angular/common";
import { Component } from "@angular/core";
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
import { firstValueFrom } from "rxjs";
import { switchMap } from "rxjs/operators";
import { combineLatest, firstValueFrom } from "rxjs";
import { LoginApprovalComponent } from "@bitwarden/auth/angular";
import { DevicesServiceAbstraction } from "@bitwarden/common/auth/abstractions/devices/devices.service.abstraction";
import {
DevicePendingAuthRequest,
DeviceResponse,
} from "@bitwarden/common/auth/abstractions/devices/responses/device.response";
import { DeviceView } from "@bitwarden/common/auth/abstractions/devices/views/device.view";
import { DeviceType, DeviceTypeMetadata } from "@bitwarden/common/enums";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
@@ -26,7 +30,8 @@ interface DeviceTableData {
loginStatus: string;
firstLogin: Date;
trusted: boolean;
devicePendingAuthRequest: object | null;
devicePendingAuthRequest: DevicePendingAuthRequest | null;
hasPendingAuthRequest: boolean;
}
/**
@@ -52,28 +57,25 @@ export class DeviceManagementComponent {
private toastService: ToastService,
private validationService: ValidationService,
) {
this.devicesService
.getCurrentDevice$()
.pipe(
takeUntilDestroyed(),
switchMap((currentDevice) => {
this.currentDevice = new DeviceView(currentDevice);
return this.devicesService.getDevices$();
}),
)
combineLatest([this.devicesService.getCurrentDevice$(), this.devicesService.getDevices$()])
.pipe(takeUntilDestroyed())
.subscribe({
next: (devices) => {
this.dataSource.data = devices.map((device) => {
next: ([currentDevice, devices]: [DeviceResponse, Array<DeviceView>]) => {
this.currentDevice = new DeviceView(currentDevice);
this.dataSource.data = devices.map((device: DeviceView): DeviceTableData => {
return {
id: device.id,
type: device.type,
displayName: this.getHumanReadableDeviceType(device.type),
loginStatus: this.getLoginStatus(device),
devicePendingAuthRequest: device.response.devicePendingAuthRequest,
firstLogin: new Date(device.creationDate),
trusted: device.response.isTrusted,
devicePendingAuthRequest: device.response.devicePendingAuthRequest,
hasPendingAuthRequest: this.hasPendingAuthRequest(device.response),
};
});
this.loading = false;
},
error: () => {
@@ -176,15 +178,36 @@ export class DeviceManagementComponent {
/**
* Check if a device has a pending auth request
* @param device - The device
* @param device - The device response
* @returns True if the device has a pending auth request, false otherwise
*/
protected hasPendingAuthRequest(device: DeviceTableData): boolean {
private hasPendingAuthRequest(device: DeviceResponse): boolean {
return (
device.devicePendingAuthRequest !== undefined && device.devicePendingAuthRequest !== null
);
}
/**
* Open a dialog to approve or deny a pending auth request for a device
*/
async managePendingAuthRequest(device: DeviceTableData) {
if (device.devicePendingAuthRequest === undefined || device.devicePendingAuthRequest === null) {
return;
}
const dialogRef = LoginApprovalComponent.open(this.dialogService, {
notificationId: device.devicePendingAuthRequest.id,
});
const result = await firstValueFrom(dialogRef.closed);
if (result !== undefined && typeof result === "boolean") {
// auth request approved or denied so reset
device.devicePendingAuthRequest = null;
device.hasPendingAuthRequest = false;
}
}
/**
* Remove a device
* @param device - The device

View File

@@ -30,7 +30,7 @@
}}</span
>
<!-- Plan Interval Toggle -->
<div class="tw-inline-block">
<div class="tw-inline-block" *ngIf="!isSubscriptionCanceled">
<bit-toggle-group
[selected]="selectedInterval"
(selectedChange)="updateInterval($event)"

View File

@@ -3302,6 +3302,9 @@
"inviteSingleEmailDesc": {
"message": "You have 1 invite remaining."
},
"inviteZeroEmailDesc": {
"message": "You have 0 invites remaining."
},
"userUsingTwoStep": {
"message": "This user is using two-step login to protect their account."
},
@@ -3810,6 +3813,67 @@
"trusted": {
"message": "Trusted"
},
"needsApproval": {
"message": "Needs approval"
},
"areYouTryingtoLogin": {
"message": "Are you trying to log in?"
},
"logInAttemptBy": {
"message": "Login attempt by $EMAIL$",
"placeholders": {
"email": {
"content": "$1",
"example": "name@example.com"
}
}
},
"deviceType": {
"message": "Device Type"
},
"ipAddress": {
"message": "IP Address"
},
"confirmLogIn": {
"message": "Confirm login"
},
"denyLogIn": {
"message": "Deny login"
},
"thisRequestIsNoLongerValid": {
"message": "This request is no longer valid."
},
"logInConfirmedForEmailOnDevice": {
"message": "Login confirmed for $EMAIL$ on $DEVICE$",
"placeholders": {
"email": {
"content": "$1",
"example": "name@example.com"
},
"device": {
"content": "$2",
"example": "iOS"
}
}
},
"youDeniedALogInAttemptFromAnotherDevice": {
"message": "You denied a login attempt from another device. If this really was you, try to log in with the device again."
},
"loginRequestHasAlreadyExpired": {
"message": "Login request has already expired."
},
"justNow": {
"message": "Just now"
},
"requestedXMinutesAgo": {
"message": "Requested $MINUTES$ minutes ago",
"placeholders": {
"minutes": {
"content": "$1",
"example": "5"
}
}
},
"creatingAccountOn": {
"message": "Creating account on"
},

View File

@@ -157,7 +157,7 @@ export class TwoFactorComponent extends CaptchaProtectedComponent implements OnI
this.toastService.showToast({
variant: "error",
title: this.i18nService.t("errorOccurred"),
message: error,
message: this.i18nService.t("webauthnCancelOrTimeout"),
});
},
(info: string) => {

View File

@@ -20,6 +20,7 @@ import {
DefaultLoginComponentService,
LoginDecryptionOptionsService,
DefaultLoginDecryptionOptionsService,
DefaultLoginApprovalComponentService,
} from "@bitwarden/auth/angular";
import {
AuthRequestServiceAbstraction,
@@ -39,6 +40,7 @@ import {
DefaultAuthRequestApiService,
DefaultLoginSuccessHandlerService,
LoginSuccessHandlerService,
LoginApprovalComponentServiceAbstraction,
} from "@bitwarden/auth/common";
import { ApiService as ApiServiceAbstraction } from "@bitwarden/common/abstractions/api.service";
import { AuditService as AuditServiceAbstraction } from "@bitwarden/common/abstractions/audit.service";
@@ -1405,6 +1407,11 @@ const safeProviders: SafeProvider[] = [
useClass: DefaultAuthRequestApiService,
deps: [ApiServiceAbstraction, LogService],
}),
safeProvider({
provide: LoginApprovalComponentServiceAbstraction,
useClass: DefaultLoginApprovalComponentService,
deps: [],
}),
safeProvider({
provide: LoginDecryptionOptionsService,
useClass: DefaultLoginDecryptionOptionsService,

View File

@@ -10,7 +10,7 @@
loading="lazy"
/>
<i
class="tw-text-muted bwi bwi-lg {{ data.icon }}"
class="tw-w-6 tw-text-muted bwi bwi-lg {{ data.icon }}"
*ngIf="!data.imageEnabled || !data.image"
></i>
</ng-container>

View File

@@ -1,23 +1,31 @@
<bit-dialog>
<span bitDialogTitle>{{ "areYouTryingtoLogin" | i18n }}</span>
<ng-container bitDialogContent>
<h4 class="tw-mb-3">{{ "logInAttemptBy" | i18n: email }}</h4>
<div>
<b>{{ "fingerprintPhraseHeader" | i18n }}</b>
<p class="tw-text-code">{{ fingerprintPhrase }}</p>
</div>
<div>
<b>{{ "deviceType" | i18n }}</b>
<p>{{ authRequestResponse?.requestDeviceType }}</p>
</div>
<div>
<b>{{ "ipAddress" | i18n }}</b>
<p>{{ authRequestResponse?.requestIpAddress }}</p>
</div>
<div>
<b>{{ "time" | i18n }}</b>
<p>{{ requestTimeText }}</p>
</div>
<ng-container *ngIf="loading">
<div class="tw-flex tw-items-center tw-justify-center" *ngIf="loading">
<i class="bwi bwi-spinner bwi-spin bwi-3x" aria-hidden="true"></i>
</div>
</ng-container>
<ng-container *ngIf="!loading">
<h4 class="tw-mb-3">{{ "logInAttemptBy" | i18n: email }}</h4>
<div>
<b>{{ "fingerprintPhraseHeader" | i18n }}</b>
<p class="tw-text-code">{{ fingerprintPhrase }}</p>
</div>
<div>
<b>{{ "deviceType" | i18n }}</b>
<p>{{ authRequestResponse?.requestDeviceType }}</p>
</div>
<div>
<b>{{ "ipAddress" | i18n }}</b>
<p>{{ authRequestResponse?.requestIpAddress }}</p>
</div>
<div>
<b>{{ "time" | i18n }}</b>
<p>{{ requestTimeText }}</p>
</div>
</ng-container>
</ng-container>
<ng-container bitDialogFooter>
<button
@@ -25,7 +33,7 @@
type="button"
buttonType="primary"
[bitAction]="approveLogin"
[bitDialogClose]="true"
[disabled]="loading"
>
{{ "confirmLogIn" | i18n }}
</button>
@@ -34,7 +42,7 @@
type="button"
buttonType="secondary"
[bitAction]="denyLogin"
[bitDialogClose]="true"
[disabled]="loading"
>
{{ "denyLogIn" | i18n }}
</button>

View File

@@ -13,6 +13,7 @@ import { AuthRequestResponse } from "@bitwarden/common/auth/models/response/auth
import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service";
import { UserId } from "@bitwarden/common/types/guid";
import { ToastService } from "@bitwarden/components";
import { KeyService } from "@bitwarden/key-management";
@@ -29,6 +30,7 @@ describe("LoginApprovalComponent", () => {
let i18nService: MockProxy<I18nService>;
let dialogRef: MockProxy<DialogRef>;
let toastService: MockProxy<ToastService>;
let validationService: MockProxy<ValidationService>;
const testNotificationId = "test-notification-id";
const testEmail = "test@bitwarden.com";
@@ -41,6 +43,7 @@ describe("LoginApprovalComponent", () => {
i18nService = mock<I18nService>();
dialogRef = mock<DialogRef>();
toastService = mock<ToastService>();
validationService = mock<ValidationService>();
accountService.activeAccount$ = of({
email: testEmail,
@@ -62,6 +65,7 @@ describe("LoginApprovalComponent", () => {
{ provide: KeyService, useValue: mock<KeyService>() },
{ provide: DialogRef, useValue: dialogRef },
{ provide: ToastService, useValue: toastService },
{ provide: ValidationService, useValue: validationService },
{
provide: LoginApprovalComponentServiceAbstraction,
useValue: mock<LoginApprovalComponentServiceAbstraction>(),

View File

@@ -16,6 +16,7 @@ import { AuthRequestResponse } from "@bitwarden/common/auth/models/response/auth
import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import {
AsyncActionsModule,
@@ -40,6 +41,8 @@ export interface LoginApprovalDialogParams {
imports: [CommonModule, AsyncActionsModule, ButtonModule, DialogModule, JslibModule],
})
export class LoginApprovalComponent implements OnInit, OnDestroy {
loading = true;
notificationId: string;
private destroy$ = new Subject<void>();
@@ -62,25 +65,25 @@ export class LoginApprovalComponent implements OnInit, OnDestroy {
private dialogRef: DialogRef,
private toastService: ToastService,
private loginApprovalComponentService: LoginApprovalComponentService,
private validationService: ValidationService,
) {
this.notificationId = params.notificationId;
}
async ngOnDestroy(): Promise<void> {
clearInterval(this.interval);
const closedWithButton = await firstValueFrom(this.dialogRef.closed);
if (!closedWithButton) {
// 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.retrieveAuthRequestAndRespond(false);
}
this.destroy$.next();
this.destroy$.complete();
}
async ngOnInit() {
if (this.notificationId != null) {
this.authRequestResponse = await this.apiService.getAuthRequest(this.notificationId);
try {
this.authRequestResponse = await this.apiService.getAuthRequest(this.notificationId);
} catch (error) {
this.validationService.showError(error);
}
const publicKey = Utils.fromB64ToArray(this.authRequestResponse.publicKey);
this.email = await await firstValueFrom(
this.accountService.activeAccount$.pipe(map((a) => a?.email)),
@@ -96,6 +99,8 @@ export class LoginApprovalComponent implements OnInit, OnDestroy {
}, RequestTimeUpdate);
this.loginApprovalComponentService.showLoginRequestedAlertIfWindowNotVisible(this.email);
this.loading = false;
}
}
@@ -131,6 +136,8 @@ export class LoginApprovalComponent implements OnInit, OnDestroy {
);
this.showResultToast(loginResponse);
}
this.dialogRef.close(approve);
}
showResultToast(loginResponse: AuthRequestResponse) {

View File

@@ -1,7 +1,6 @@
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import { ListResponse } from "@bitwarden/common/models/response/list.response";
import { ListResponse } from "../../../models/response/list.response";
import { OrganizationDomainRequest } from "../../services/organization-domain/requests/organization-domain.request";
import { OrganizationDomainSsoDetailsResponse } from "./responses/organization-domain-sso-details.response";

View File

@@ -1,4 +1,4 @@
import { BaseResponse } from "@bitwarden/common/models/response/base.response";
import { BaseResponse } from "../../../../models/response/base.response";
export class VerifiedOrganizationDomainSsoDetailsResponse extends BaseResponse {
organizationName: string;

View File

@@ -1,7 +1,5 @@
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import { BillingHistoryResponse } from "@bitwarden/common/billing/models/response/billing-history.response";
import { OrganizationApiKeyRequest } from "../../../admin-console/models/request/organization-api-key.request";
import { OrganizationSsoRequest } from "../../../auth/models/request/organization-sso.request";
import { SecretVerificationRequest } from "../../../auth/models/request/secret-verification.request";
@@ -13,6 +11,7 @@ import { OrganizationSmSubscriptionUpdateRequest } from "../../../billing/models
import { OrganizationSubscriptionUpdateRequest } from "../../../billing/models/request/organization-subscription-update.request";
import { PaymentRequest } from "../../../billing/models/request/payment.request";
import { SecretsManagerSubscribeRequest } from "../../../billing/models/request/sm-subscribe.request";
import { BillingHistoryResponse } from "../../../billing/models/response/billing-history.response";
import { BillingResponse } from "../../../billing/models/response/billing.response";
import { OrganizationSubscriptionResponse } from "../../../billing/models/response/organization-subscription.response";
import { PaymentResponse } from "../../../billing/models/response/payment.response";

View File

@@ -1,14 +1,13 @@
import { mock } from "jest-mock-extended";
import { lastValueFrom } from "rxjs";
import { VerifiedOrganizationDomainSsoDetailsResponse } from "@bitwarden/common/admin-console/abstractions/organization-domain/responses/verified-organization-domain-sso-details.response";
import { ListResponse } from "@bitwarden/common/models/response/list.response";
import { ApiService } from "../../../abstractions/api.service";
import { ListResponse } from "../../../models/response/list.response";
import { I18nService } from "../../../platform/abstractions/i18n.service";
import { PlatformUtilsService } from "../../../platform/abstractions/platform-utils.service";
import { OrganizationDomainSsoDetailsResponse } from "../../abstractions/organization-domain/responses/organization-domain-sso-details.response";
import { OrganizationDomainResponse } from "../../abstractions/organization-domain/responses/organization-domain.response";
import { VerifiedOrganizationDomainSsoDetailsResponse } from "../../abstractions/organization-domain/responses/verified-organization-domain-sso-details.response";
import { OrgDomainApiService } from "./org-domain-api.service";
import { OrgDomainService } from "./org-domain.service";

View File

@@ -1,6 +1,5 @@
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import { BillingHistoryResponse } from "@bitwarden/common/billing/models/response/billing-history.response";
import { ApiService } from "../../../abstractions/api.service";
import { OrganizationApiKeyRequest } from "../../../admin-console/models/request/organization-api-key.request";
@@ -14,6 +13,7 @@ import { OrganizationSmSubscriptionUpdateRequest } from "../../../billing/models
import { OrganizationSubscriptionUpdateRequest } from "../../../billing/models/request/organization-subscription-update.request";
import { PaymentRequest } from "../../../billing/models/request/payment.request";
import { SecretsManagerSubscribeRequest } from "../../../billing/models/request/sm-subscribe.request";
import { BillingHistoryResponse } from "../../../billing/models/response/billing-history.response";
import { BillingResponse } from "../../../billing/models/response/billing.response";
import { OrganizationSubscriptionResponse } from "../../../billing/models/response/organization-subscription.response";
import { PaymentResponse } from "../../../billing/models/response/payment.response";

View File

@@ -1,7 +1,6 @@
import { Jsonify } from "type-fest";
import { ORGANIZATIONS_DISK, UserKeyDefinition } from "@bitwarden/common/platform/state";
import { ORGANIZATIONS_DISK, UserKeyDefinition } from "../../../platform/state";
import { OrganizationData } from "../../models/data/organization.data";
/**

View File

@@ -1,6 +1,11 @@
import { DeviceType } from "../../../../enums";
import { BaseResponse } from "../../../../models/response/base.response";
export interface DevicePendingAuthRequest {
id: string;
creationDate: string;
}
export class DeviceResponse extends BaseResponse {
id: string;
userId: string;
@@ -10,7 +15,7 @@ export class DeviceResponse extends BaseResponse {
creationDate: string;
revisionDate: string;
isTrusted: boolean;
devicePendingAuthRequest: { id: string; creationDate: string } | null;
devicePendingAuthRequest: DevicePendingAuthRequest | null;
constructor(response: any) {
super(response);

View File

@@ -1,10 +1,10 @@
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import { EncString } from "@bitwarden/common/platform/models/domain/enc-string";
// FIXME: remove `src` and fix import
// eslint-disable-next-line no-restricted-imports
import { RotateableKeySet } from "../../../../../auth/src/common/models";
import { EncString } from "../../../platform/models/domain/enc-string";
export class WebauthnRotateCredentialRequest {
id: string;

View File

@@ -1,8 +1,7 @@
import { Observable, defer, map } from "rxjs";
import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service";
import { ListResponse } from "../../../models/response/list.response";
import { AppIdService } from "../../../platform/abstractions/app-id.service";
import { DevicesServiceAbstraction } from "../../abstractions/devices/devices.service.abstraction";
import { DeviceResponse } from "../../abstractions/devices/responses/device.response";
import { DeviceView } from "../../abstractions/devices/views/device.view";

View File

@@ -2,10 +2,9 @@
// @ts-strict-ignore
import { firstValueFrom, map, Observable } from "rxjs";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { EncryptService } from "../../../platform/abstractions/encrypt.service";
import { KeyGenerationService } from "../../../platform/abstractions/key-generation.service";
import { LogService } from "../../../platform/abstractions/log.service";
import { StateService } from "../../../platform/abstractions/state.service";
import { EncryptionType } from "../../../platform/enums";
import { EncryptedString, EncString } from "../../../platform/models/domain/enc-string";

View File

@@ -2,17 +2,15 @@ import { mock, MockProxy } from "jest-mock-extended";
import { BehaviorSubject } from "rxjs";
import { OrganizationUserApiService } from "@bitwarden/admin-console/common";
import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
// FIXME: remove `src` and fix import
// eslint-disable-next-line no-restricted-imports
import { UserId } from "../../../../common/src/types/guid";
// FIXME: remove `src` and fix import
// eslint-disable-next-line no-restricted-imports
import { KeyService } from "../../../../key-management/src/abstractions/key.service";
import { OrganizationApiServiceAbstraction } from "../../admin-console/abstractions/organization/organization-api.service.abstraction";
import { OrganizationAutoEnrollStatusResponse } from "../../admin-console/models/response/organization-auto-enroll-status.response";
import { EncryptService } from "../../platform/abstractions/encrypt.service";
import { I18nService } from "../../platform/abstractions/i18n.service";
import { UserId } from "../../types/guid";
import { Account, AccountInfo, AccountService } from "../abstractions/account.service";
import { PasswordResetEnrollmentServiceImplementation } from "./password-reset-enrollment.service.implementation";

View File

@@ -6,12 +6,12 @@ import {
OrganizationUserApiService,
OrganizationUserResetPasswordEnrollmentRequest,
} from "@bitwarden/admin-console/common";
import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
// FIXME: remove `src` and fix import
// eslint-disable-next-line no-restricted-imports
import { KeyService } from "../../../../key-management/src/abstractions/key.service";
import { OrganizationApiServiceAbstraction } from "../../admin-console/abstractions/organization/organization-api.service.abstraction";
import { EncryptService } from "../../platform/abstractions/encrypt.service";
import { I18nService } from "../../platform/abstractions/i18n.service";
import { Utils } from "../../platform/misc/utils";
import { UserKey } from "../../types/key";

View File

@@ -1,9 +1,8 @@
import { MockProxy, mock } from "jest-mock-extended";
import { firstValueFrom, of } from "rxjs";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { FakeStateProvider, FakeAccountService, mockAccountServiceWith } from "../../../spec";
import { ConfigService } from "../../platform/abstractions/config/config.service";
import { Utils } from "../../platform/misc/utils";
import { UserId } from "../../types/guid";

View File

@@ -2,8 +2,7 @@
// @ts-strict-ignore
import { map, Observable, switchMap, of } from "rxjs";
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { FeatureFlag } from "../../enums/feature-flag.enum";
import {
NeverDomains,
EquivalentDomains,

View File

@@ -1,9 +1,6 @@
import {
normalizeExpiryYearFormat,
isCardExpired,
parseYearMonthExpiry,
} from "@bitwarden/common/autofill/utils";
import { CardView } from "@bitwarden/common/vault/models/view/card.view";
import { CardView } from "../vault/models/view/card.view";
import { normalizeExpiryYearFormat, isCardExpired, parseYearMonthExpiry } from "./utils";
function getExpiryYearValueFormats(currentCentury: string) {
return [

View File

@@ -1,11 +1,12 @@
import { CardView } from "../vault/models/view/card.view";
import {
DelimiterPatternExpression,
ExpiryFullYearPattern,
ExpiryFullYearPatternExpression,
IrrelevantExpiryCharactersPatternExpression,
MonthPatternExpression,
} from "@bitwarden/common/autofill/constants";
import { CardView } from "@bitwarden/common/vault/models/view/card.view";
} from "./constants";
type NonZeroIntegers = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;
type Year = `${NonZeroIntegers}${NonZeroIntegers}${0 | NonZeroIntegers}${0 | NonZeroIntegers}`;

View File

@@ -3,7 +3,7 @@
import {
BillingInvoiceResponse,
BillingTransactionResponse,
} from "@bitwarden/common/billing/models/response/billing.response";
} from "../../models/response/billing.response";
export class AccountBillingApiServiceAbstraction {
getBillingInvoices: (status?: string, startAfter?: string) => Promise<BillingInvoiceResponse[]>;

View File

@@ -1,20 +1,20 @@
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import { ProviderOrganizationOrganizationDetailsResponse } from "@bitwarden/common/admin-console/models/response/provider/provider-organization.response";
import { PaymentMethodType } from "@bitwarden/common/billing/enums";
import { ExpandedTaxInfoUpdateRequest } from "@bitwarden/common/billing/models/request/expanded-tax-info-update.request";
import { UpdatePaymentMethodRequest } from "@bitwarden/common/billing/models/request/update-payment-method.request";
import { VerifyBankAccountRequest } from "@bitwarden/common/billing/models/request/verify-bank-account.request";
import { InvoicesResponse } from "@bitwarden/common/billing/models/response/invoices.response";
import { PaymentMethodResponse } from "@bitwarden/common/billing/models/response/payment-method.response";
import { OrganizationCreateRequest } from "../../admin-console/models/request/organization-create.request";
import { ProviderOrganizationOrganizationDetailsResponse } from "../../admin-console/models/response/provider/provider-organization.response";
import { SubscriptionCancellationRequest } from "../../billing/models/request/subscription-cancellation.request";
import { OrganizationBillingMetadataResponse } from "../../billing/models/response/organization-billing-metadata.response";
import { PlanResponse } from "../../billing/models/response/plan.response";
import { ListResponse } from "../../models/response/list.response";
import { PaymentMethodType } from "../enums";
import { CreateClientOrganizationRequest } from "../models/request/create-client-organization.request";
import { ExpandedTaxInfoUpdateRequest } from "../models/request/expanded-tax-info-update.request";
import { UpdateClientOrganizationRequest } from "../models/request/update-client-organization.request";
import { UpdatePaymentMethodRequest } from "../models/request/update-payment-method.request";
import { VerifyBankAccountRequest } from "../models/request/verify-bank-account.request";
import { InvoicesResponse } from "../models/response/invoices.response";
import { PaymentMethodResponse } from "../models/response/payment-method.response";
import { ProviderSubscriptionResponse } from "../models/response/provider-subscription-response";
export abstract class BillingApiServiceAbstraction {

View File

@@ -1,11 +1,11 @@
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import { BillingSourceResponse } from "@bitwarden/common/billing/models/response/billing.response";
import { PaymentSourceResponse } from "@bitwarden/common/billing/models/response/payment-source.response";
import { OrganizationResponse } from "../../admin-console/models/response/organization.response";
import { InitiationPath } from "../../models/request/reference-event.request";
import { PaymentMethodType, PlanType } from "../enums";
import { BillingSourceResponse } from "../models/response/billing.response";
import { PaymentSourceResponse } from "../models/response/payment-source.response";
export type OrganizationInformation = {
name: string;

View File

@@ -3,7 +3,7 @@
import {
BillingInvoiceResponse,
BillingTransactionResponse,
} from "@bitwarden/common/billing/models/response/billing.response";
} from "../../models/response/billing.response";
export class OrganizationBillingApiServiceAbstraction {
getBillingInvoices: (

View File

@@ -1,7 +1,7 @@
import { CountryListItem } from "@bitwarden/common/billing/models/domain";
import { PreviewIndividualInvoiceRequest } from "@bitwarden/common/billing/models/request/preview-individual-invoice.request";
import { PreviewOrganizationInvoiceRequest } from "@bitwarden/common/billing/models/request/preview-organization-invoice.request";
import { PreviewInvoiceResponse } from "@bitwarden/common/billing/models/response/preview-invoice.response";
import { CountryListItem } from "../models/domain";
import { PreviewIndividualInvoiceRequest } from "../models/request/preview-individual-invoice.request";
import { PreviewOrganizationInvoiceRequest } from "../models/request/preview-organization-invoice.request";
import { PreviewInvoiceResponse } from "../models/response/preview-invoice.response";
export abstract class TaxServiceAbstraction {
abstract getCountries(): CountryListItem[];

View File

@@ -1,6 +1,6 @@
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import { TaxInfoResponse } from "@bitwarden/common/billing/models/response/tax-info.response";
import { TaxInfoResponse } from "../response/tax-info.response";
export class TaxInformation {
country: string;

View File

@@ -1,6 +1,6 @@
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import { TaxInformation } from "@bitwarden/common/billing/models/domain/tax-information";
import { TaxInformation } from "../domain/tax-information";
import { TaxInfoUpdateRequest } from "./tax-info-update.request";

View File

@@ -1,4 +1,4 @@
import { PlanType } from "@bitwarden/common/billing/enums";
import { PlanType } from "../../enums";
export class PreviewOrganizationInvoiceRequest {
organizationId?: string;

View File

@@ -1,6 +1,6 @@
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import { PaymentMethodType } from "@bitwarden/common/billing/enums";
import { PaymentMethodType } from "../../enums";
export class TokenizedPaymentSourceRequest {
type: PaymentMethodType;

View File

@@ -1,7 +1,7 @@
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import { ExpandedTaxInfoUpdateRequest } from "@bitwarden/common/billing/models/request/expanded-tax-info-update.request";
import { TokenizedPaymentSourceRequest } from "@bitwarden/common/billing/models/request/tokenized-payment-source.request";
import { ExpandedTaxInfoUpdateRequest } from "./expanded-tax-info-update.request";
import { TokenizedPaymentSourceRequest } from "./tokenized-payment-source.request";
export class UpdatePaymentMethodRequest {
paymentSource: TokenizedPaymentSourceRequest;

View File

@@ -1,4 +1,4 @@
import { BaseResponse } from "@bitwarden/common/models/response/base.response";
import { BaseResponse } from "../../../models/response/base.response";
export class InvoicesResponse extends BaseResponse {
invoices: InvoiceResponse[] = [];

View File

@@ -1,5 +1,5 @@
import { PaymentMethodType } from "@bitwarden/common/billing/enums";
import { BaseResponse } from "@bitwarden/common/models/response/base.response";
import { BaseResponse } from "../../../models/response/base.response";
import { PaymentMethodType } from "../../enums";
export class PaymentSourceResponse extends BaseResponse {
type: PaymentMethodType;

View File

@@ -1,4 +1,4 @@
import { BaseResponse } from "@bitwarden/common/models/response/base.response";
import { BaseResponse } from "../../../models/response/base.response";
export class PreviewInvoiceResponse extends BaseResponse {
effectiveTaxRate: number;

View File

@@ -1,9 +1,9 @@
import { ProviderType } from "@bitwarden/common/admin-console/enums";
import { PlanType, ProductTierType } from "@bitwarden/common/billing/enums";
import { SubscriptionSuspensionResponse } from "@bitwarden/common/billing/models/response/subscription-suspension.response";
import { TaxInfoResponse } from "@bitwarden/common/billing/models/response/tax-info.response";
import { ProviderType } from "../../../admin-console/enums";
import { BaseResponse } from "../../../models/response/base.response";
import { PlanType, ProductTierType } from "../../enums";
import { SubscriptionSuspensionResponse } from "./subscription-suspension.response";
import { TaxInfoResponse } from "./tax-info.response";
export class ProviderSubscriptionResponse extends BaseResponse {
status: string;

View File

@@ -1,4 +1,4 @@
import { BaseResponse } from "@bitwarden/common/models/response/base.response";
import { BaseResponse } from "../../../models/response/base.response";
export class SubscriptionSuspensionResponse extends BaseResponse {
suspensionDate: string;

View File

@@ -1,4 +1,4 @@
import { BaseResponse } from "@bitwarden/common/models/response/base.response";
import { BaseResponse } from "../../../models/response/base.response";
export class TaxIdTypesResponse extends BaseResponse {
taxIdTypes: TaxIdTypeResponse[] = [];

View File

@@ -1,17 +1,16 @@
import { firstValueFrom } from "rxjs";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { BillingHistoryResponse } from "@bitwarden/common/billing/models/response/billing-history.response";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import {
FakeAccountService,
mockAccountServiceWith,
FakeStateProvider,
FakeSingleUserState,
} from "../../../../spec";
import { ApiService } from "../../../abstractions/api.service";
import { PlatformUtilsService } from "../../../platform/abstractions/platform-utils.service";
import { UserId } from "../../../types/guid";
import { BillingAccountProfile } from "../../abstractions/account/billing-account-profile-state.service";
import { BillingHistoryResponse } from "../../models/response/billing-history.response";
import {
BILLING_ACCOUNT_PROFILE_KEY_DEFINITION,

View File

@@ -1,8 +1,7 @@
import { map, Observable, combineLatest, concatMap } from "rxjs";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { ApiService } from "../../../abstractions/api.service";
import { PlatformUtilsService } from "../../../platform/abstractions/platform-utils.service";
import { BILLING_DISK, StateProvider, UserKeyDefinition } from "../../../platform/state";
import { UserId } from "../../../types/guid";
import {

View File

@@ -1,25 +1,25 @@
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import { ProviderOrganizationOrganizationDetailsResponse } from "@bitwarden/common/admin-console/models/response/provider/provider-organization.response";
import { UpdatePaymentMethodRequest } from "@bitwarden/common/billing/models/request/update-payment-method.request";
import { VerifyBankAccountRequest } from "@bitwarden/common/billing/models/request/verify-bank-account.request";
import { InvoicesResponse } from "@bitwarden/common/billing/models/response/invoices.response";
import { PaymentMethodResponse } from "@bitwarden/common/billing/models/response/payment-method.response";
import { ErrorResponse } from "@bitwarden/common/models/response/error.response";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { ToastService } from "@bitwarden/components";
import { ApiService } from "../../abstractions/api.service";
import { OrganizationCreateRequest } from "../../admin-console/models/request/organization-create.request";
import { BillingApiServiceAbstraction } from "../../billing/abstractions";
import { PaymentMethodType } from "../../billing/enums";
import { ExpandedTaxInfoUpdateRequest } from "../../billing/models/request/expanded-tax-info-update.request";
import { SubscriptionCancellationRequest } from "../../billing/models/request/subscription-cancellation.request";
import { OrganizationBillingMetadataResponse } from "../../billing/models/response/organization-billing-metadata.response";
import { PlanResponse } from "../../billing/models/response/plan.response";
import { ProviderOrganizationOrganizationDetailsResponse } from "../../admin-console/models/response/provider/provider-organization.response";
import { ErrorResponse } from "../../models/response/error.response";
import { ListResponse } from "../../models/response/list.response";
import { LogService } from "../../platform/abstractions/log.service";
import { BillingApiServiceAbstraction } from "../abstractions";
import { PaymentMethodType } from "../enums";
import { CreateClientOrganizationRequest } from "../models/request/create-client-organization.request";
import { ExpandedTaxInfoUpdateRequest } from "../models/request/expanded-tax-info-update.request";
import { SubscriptionCancellationRequest } from "../models/request/subscription-cancellation.request";
import { UpdateClientOrganizationRequest } from "../models/request/update-client-organization.request";
import { UpdatePaymentMethodRequest } from "../models/request/update-payment-method.request";
import { VerifyBankAccountRequest } from "../models/request/verify-bank-account.request";
import { InvoicesResponse } from "../models/response/invoices.response";
import { OrganizationBillingMetadataResponse } from "../models/response/organization-billing-metadata.response";
import { PaymentMethodResponse } from "../models/response/payment-method.response";
import { PlanResponse } from "../models/response/plan.response";
import { ProviderSubscriptionResponse } from "../models/response/provider-subscription-response";
export class BillingApiService implements BillingApiServiceAbstraction {

View File

@@ -1,18 +1,5 @@
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import {
BillingApiServiceAbstraction,
OrganizationBillingServiceAbstraction,
OrganizationInformation,
PaymentInformation,
PlanInformation,
SubscriptionInformation,
} from "@bitwarden/common/billing/abstractions";
import { BillingSourceResponse } from "@bitwarden/common/billing/models/response/billing.response";
import { PaymentSourceResponse } from "@bitwarden/common/billing/models/response/payment-source.response";
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { SyncService } from "@bitwarden/common/platform/sync";
import { KeyService } from "@bitwarden/key-management";
import { ApiService } from "../../abstractions/api.service";
@@ -20,12 +7,25 @@ import { OrganizationApiServiceAbstraction as OrganizationApiService } from "../
import { OrganizationCreateRequest } from "../../admin-console/models/request/organization-create.request";
import { OrganizationKeysRequest } from "../../admin-console/models/request/organization-keys.request";
import { OrganizationResponse } from "../../admin-console/models/response/organization.response";
import { FeatureFlag } from "../../enums/feature-flag.enum";
import { ConfigService } from "../../platform/abstractions/config/config.service";
import { EncryptService } from "../../platform/abstractions/encrypt.service";
import { I18nService } from "../../platform/abstractions/i18n.service";
import { EncString } from "../../platform/models/domain/enc-string";
import { SyncService } from "../../platform/sync";
import { OrgKey } from "../../types/key";
import {
BillingApiServiceAbstraction,
OrganizationBillingServiceAbstraction,
OrganizationInformation,
PaymentInformation,
PlanInformation,
SubscriptionInformation,
} from "../abstractions";
import { PlanType } from "../enums";
import { OrganizationNoPaymentMethodCreateRequest } from "../models/request/organization-no-payment-method-create-request";
import { BillingSourceResponse } from "../models/response/billing.response";
import { PaymentSourceResponse } from "../models/response/payment-source.response";
interface OrganizationKeys {
encryptedKey: EncString;

View File

@@ -1,9 +1,9 @@
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { TaxServiceAbstraction } from "@bitwarden/common/billing/abstractions/tax.service.abstraction";
import { CountryListItem } from "@bitwarden/common/billing/models/domain";
import { PreviewIndividualInvoiceRequest } from "@bitwarden/common/billing/models/request/preview-individual-invoice.request";
import { PreviewOrganizationInvoiceRequest } from "@bitwarden/common/billing/models/request/preview-organization-invoice.request";
import { PreviewInvoiceResponse } from "@bitwarden/common/billing/models/response/preview-invoice.response";
import { ApiService } from "../../abstractions/api.service";
import { TaxServiceAbstraction } from "../abstractions/tax.service.abstraction";
import { CountryListItem } from "../models/domain";
import { PreviewIndividualInvoiceRequest } from "../models/request/preview-individual-invoice.request";
import { PreviewOrganizationInvoiceRequest } from "../models/request/preview-organization-invoice.request";
import { PreviewInvoiceResponse } from "../models/response/preview-invoice.response";
export class TaxService implements TaxServiceAbstraction {
constructor(private apiService: ApiService) {}

View File

@@ -4,6 +4,17 @@
* Flags MUST be short lived and SHALL be removed once enabled.
*/
export enum FeatureFlag {
/* Autofill */
BlockBrowserInjectionsByDomain = "block-browser-injections-by-domain",
DelayFido2PageScriptInitWithinMv2 = "delay-fido2-page-script-init-within-mv2",
EnableNewCardCombinedExpiryAutofill = "enable-new-card-combined-expiry-autofill",
GenerateIdentityFillScriptRefactor = "generate-identity-fill-script-refactor",
InlineMenuFieldQualification = "inline-menu-field-qualification",
InlineMenuPositioningImprovements = "inline-menu-positioning-improvements",
InlineMenuTotp = "inline-menu-totp",
NotificationBarAddLoginImprovements = "notification-bar-add-login-improvements",
UseTreeWalkerApiForPageDetailsCollection = "use-tree-walker-api-for-page-details-collection",
BrowserFilelessImport = "browser-fileless-import",
ItemShare = "item-share",
GeneratorToolsModernization = "generator-tools-modernization",
@@ -11,23 +22,15 @@ export enum FeatureFlag {
ExtensionRefresh = "extension-refresh",
PersistPopupView = "persist-popup-view",
PM4154_BulkEncryptionService = "PM-4154-bulk-encryption-service",
UseTreeWalkerApiForPageDetailsCollection = "use-tree-walker-api-for-page-details-collection",
EmailVerification = "email-verification",
InlineMenuFieldQualification = "inline-menu-field-qualification",
TwoFactorComponentRefactor = "two-factor-component-refactor",
InlineMenuPositioningImprovements = "inline-menu-positioning-improvements",
ProviderClientVaultPrivacyBanner = "ac-2833-provider-client-vault-privacy-banner",
VaultBulkManagementAction = "vault-bulk-management-action",
IdpAutoSubmitLogin = "idp-auto-submit-login",
UnauthenticatedExtensionUIRefresh = "unauth-ui-refresh",
GenerateIdentityFillScriptRefactor = "generate-identity-fill-script-refactor",
EnableNewCardCombinedExpiryAutofill = "enable-new-card-combined-expiry-autofill",
DelayFido2PageScriptInitWithinMv2 = "delay-fido2-page-script-init-within-mv2",
AccountDeprovisioning = "pm-10308-account-deprovisioning",
SSHKeyVaultItem = "ssh-key-vault-item",
SSHAgent = "ssh-agent",
NotificationBarAddLoginImprovements = "notification-bar-add-login-improvements",
BlockBrowserInjectionsByDomain = "block-browser-injections-by-domain",
AC2476_DeprecateStripeSourcesAPI = "AC-2476-deprecate-stripe-sources-api",
CipherKeyEncryption = "cipher-key-encryption",
VerifiedSsoDomainEndpoint = "pm-12337-refactor-sso-details-endpoint",
@@ -39,7 +42,6 @@ export enum FeatureFlag {
NewDeviceVerificationTemporaryDismiss = "new-device-temporary-dismiss",
NewDeviceVerificationPermanentDismiss = "new-device-permanent-dismiss",
DisableFreeFamiliesSponsorship = "PM-12274-disable-free-families-sponsorship",
InlineMenuTotp = "inline-menu-totp",
MacOsNativeCredentialSync = "macos-native-credential-sync",
PM11360RemoveProviderExportPermission = "pm-11360-remove-provider-export-permission",
PM12443RemovePagingLogic = "pm-12443-remove-paging-logic",
@@ -59,6 +61,17 @@ const FALSE = false as boolean;
* We support true as a value as we prefer flags to "enable" not "disable".
*/
export const DefaultFeatureFlagValue = {
/* Autofill */
[FeatureFlag.BlockBrowserInjectionsByDomain]: FALSE,
[FeatureFlag.DelayFido2PageScriptInitWithinMv2]: FALSE,
[FeatureFlag.EnableNewCardCombinedExpiryAutofill]: FALSE,
[FeatureFlag.GenerateIdentityFillScriptRefactor]: FALSE,
[FeatureFlag.InlineMenuFieldQualification]: FALSE,
[FeatureFlag.InlineMenuPositioningImprovements]: FALSE,
[FeatureFlag.InlineMenuTotp]: FALSE,
[FeatureFlag.NotificationBarAddLoginImprovements]: FALSE,
[FeatureFlag.UseTreeWalkerApiForPageDetailsCollection]: FALSE,
[FeatureFlag.BrowserFilelessImport]: FALSE,
[FeatureFlag.ItemShare]: FALSE,
[FeatureFlag.GeneratorToolsModernization]: FALSE,
@@ -66,23 +79,15 @@ export const DefaultFeatureFlagValue = {
[FeatureFlag.ExtensionRefresh]: FALSE,
[FeatureFlag.PersistPopupView]: FALSE,
[FeatureFlag.PM4154_BulkEncryptionService]: FALSE,
[FeatureFlag.UseTreeWalkerApiForPageDetailsCollection]: FALSE,
[FeatureFlag.EmailVerification]: FALSE,
[FeatureFlag.InlineMenuFieldQualification]: FALSE,
[FeatureFlag.TwoFactorComponentRefactor]: FALSE,
[FeatureFlag.InlineMenuPositioningImprovements]: FALSE,
[FeatureFlag.ProviderClientVaultPrivacyBanner]: FALSE,
[FeatureFlag.VaultBulkManagementAction]: FALSE,
[FeatureFlag.IdpAutoSubmitLogin]: FALSE,
[FeatureFlag.UnauthenticatedExtensionUIRefresh]: FALSE,
[FeatureFlag.GenerateIdentityFillScriptRefactor]: FALSE,
[FeatureFlag.EnableNewCardCombinedExpiryAutofill]: FALSE,
[FeatureFlag.DelayFido2PageScriptInitWithinMv2]: FALSE,
[FeatureFlag.AccountDeprovisioning]: FALSE,
[FeatureFlag.SSHKeyVaultItem]: FALSE,
[FeatureFlag.SSHAgent]: FALSE,
[FeatureFlag.NotificationBarAddLoginImprovements]: FALSE,
[FeatureFlag.BlockBrowserInjectionsByDomain]: FALSE,
[FeatureFlag.AC2476_DeprecateStripeSourcesAPI]: FALSE,
[FeatureFlag.CipherKeyEncryption]: FALSE,
[FeatureFlag.VerifiedSsoDomainEndpoint]: FALSE,
@@ -94,7 +99,6 @@ export const DefaultFeatureFlagValue = {
[FeatureFlag.NewDeviceVerificationTemporaryDismiss]: FALSE,
[FeatureFlag.NewDeviceVerificationPermanentDismiss]: FALSE,
[FeatureFlag.DisableFreeFamiliesSponsorship]: FALSE,
[FeatureFlag.InlineMenuTotp]: FALSE,
[FeatureFlag.MacOsNativeCredentialSync]: FALSE,
[FeatureFlag.PM11360RemoveProviderExportPermission]: FALSE,
[FeatureFlag.PM12443RemovePagingLogic]: FALSE,

View File

@@ -2,8 +2,6 @@
// @ts-strict-ignore
import { firstValueFrom, map, timeout } from "rxjs";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
import { BiometricStateService } from "@bitwarden/key-management";
// FIXME: remove `src` and fix import
@@ -14,6 +12,8 @@ import { AccountService } from "../../auth/abstractions/account.service";
import { AuthService } from "../../auth/abstractions/auth.service";
import { AuthenticationStatus } from "../../auth/enums/authentication-status";
import { VaultTimeoutAction } from "../../enums/vault-timeout-action.enum";
import { LogService } from "../../platform/abstractions/log.service";
import { MessagingService } from "../../platform/abstractions/messaging.service";
import { UserId } from "../../types/guid";
import { ProcessReloadServiceAbstraction } from "../abstractions/process-reload.service";

View File

@@ -1,9 +1,9 @@
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import { SshKeyView as SshKeyView } from "@bitwarden/common/vault/models/view/ssh-key.view";
import { EncString } from "../../platform/models/domain/enc-string";
import { SshKey as SshKeyDomain } from "../../vault/models/domain/ssh-key";
import { SshKeyView as SshKeyView } from "../../vault/models/view/ssh-key.view";
import { safeGetString } from "./utils";

View File

@@ -1,4 +1,4 @@
import { EncString } from "@bitwarden/common/platform/models/domain/enc-string";
import { EncString } from "../../platform/models/domain/enc-string";
export function safeGetString(value: string | EncString) {
if (value == null) {

View File

@@ -6,8 +6,6 @@ import {
FakeUserDecryptionOptions as UserDecryptionOptions,
UserDecryptionOptionsServiceAbstraction,
} from "@bitwarden/auth/common";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { UserId } from "@bitwarden/common/types/guid";
import { BiometricStateService } from "@bitwarden/key-management";
// FIXME: remove `src` and fix import
@@ -20,10 +18,12 @@ import { Policy } from "../../admin-console/models/domain/policy";
import { TokenService } from "../../auth/abstractions/token.service";
import { VaultTimeoutAction } from "../../enums/vault-timeout-action.enum";
import { LogService } from "../../platform/abstractions/log.service";
import { Utils } from "../../platform/misc/utils";
import {
VAULT_TIMEOUT,
VAULT_TIMEOUT_ACTION,
} from "../../services/vault-timeout/vault-timeout-settings.state";
import { UserId } from "../../types/guid";
import { VaultTimeout, VaultTimeoutStringType } from "../../types/vault-timeout.type";
import { VaultTimeoutSettingsService } from "./vault-timeout-settings.service";

View File

@@ -3,8 +3,6 @@ import { BehaviorSubject, from, of } from "rxjs";
import { CollectionService } from "@bitwarden/admin-console/common";
import { LogoutReason } from "@bitwarden/auth/common";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { TaskSchedulerService } from "@bitwarden/common/platform/scheduling";
import { BiometricsService } from "@bitwarden/key-management";
import { FakeAccountService, mockAccountServiceWith } from "../../../spec/fake-account-service";
@@ -15,10 +13,12 @@ import { AuthService } from "../../auth/abstractions/auth.service";
import { AuthenticationStatus } from "../../auth/enums/authentication-status";
import { FakeMasterPasswordService } from "../../auth/services/master-password/fake-master-password.service";
import { VaultTimeoutAction } from "../../enums/vault-timeout-action.enum";
import { LogService } from "../../platform/abstractions/log.service";
import { MessagingService } from "../../platform/abstractions/messaging.service";
import { PlatformUtilsService } from "../../platform/abstractions/platform-utils.service";
import { StateService } from "../../platform/abstractions/state.service";
import { Utils } from "../../platform/misc/utils";
import { TaskSchedulerService } from "../../platform/scheduling";
import { StateEventRunnerService } from "../../platform/state";
import { UserId } from "../../types/guid";
import { VaultTimeout, VaultTimeoutStringType } from "../../types/vault-timeout.type";

View File

@@ -4,8 +4,6 @@ import { combineLatest, concatMap, filter, firstValueFrom, map, timeout } from "
import { CollectionService } from "@bitwarden/admin-console/common";
import { LogoutReason } from "@bitwarden/auth/common";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { TaskSchedulerService, ScheduledTaskNames } from "@bitwarden/common/platform/scheduling";
import { BiometricsService } from "@bitwarden/key-management";
import { SearchService } from "../../abstractions/search.service";
@@ -16,9 +14,11 @@ import { AuthService } from "../../auth/abstractions/auth.service";
import { InternalMasterPasswordServiceAbstraction } from "../../auth/abstractions/master-password.service.abstraction";
import { AuthenticationStatus } from "../../auth/enums/authentication-status";
import { VaultTimeoutAction } from "../../enums/vault-timeout-action.enum";
import { LogService } from "../../platform/abstractions/log.service";
import { MessagingService } from "../../platform/abstractions/messaging.service";
import { PlatformUtilsService } from "../../platform/abstractions/platform-utils.service";
import { StateService } from "../../platform/abstractions/state.service";
import { TaskSchedulerService, ScheduledTaskNames } from "../../platform/scheduling";
import { StateEventRunnerService } from "../../platform/state";
import { UserId } from "../../types/guid";
import { CipherService } from "../../vault/abstractions/cipher.service";

View File

@@ -1,13 +1,13 @@
import { mock } from "jest-mock-extended";
import { BehaviorSubject, Subject } from "rxjs";
import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
import { CsprngArray } from "@bitwarden/common/types/csprng";
import { OrganizationId, UserId } from "@bitwarden/common/types/guid";
import { OrgKey, UserKey } from "@bitwarden/common/types/key";
import { KeyService } from "@bitwarden/key-management";
import { EncryptService } from "../../platform/abstractions/encrypt.service";
import { SymmetricCryptoKey } from "../../platform/models/domain/symmetric-crypto-key";
import { CsprngArray } from "../../types/csprng";
import { OrganizationId, UserId } from "../../types/guid";
import { OrgKey, UserKey } from "../../types/key";
import { OrganizationBound, UserBound } from "../dependencies";
import { KeyServiceLegacyEncryptorProvider } from "./key-service-legacy-encryptor-provider";

View File

@@ -12,10 +12,10 @@ import {
takeWhile,
} from "rxjs";
import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
import { OrganizationId, UserId } from "@bitwarden/common/types/guid";
import { KeyService } from "@bitwarden/key-management";
import { EncryptService } from "../../platform/abstractions/encrypt.service";
import { OrganizationId, UserId } from "../../types/guid";
import {
OrganizationBound,
SingleOrganizationDependency,

View File

@@ -2,9 +2,8 @@
// @ts-strict-ignore
import { Jsonify } from "type-fest";
import { OrganizationId } from "@bitwarden/common/types/guid";
import { EncString } from "../../platform/models/domain/enc-string";
import { OrganizationId } from "../../types/guid";
/** An encryption strategy that protects a type's secrets with
* organization-specific keys. This strategy is bound to a specific organization.

View File

@@ -2,10 +2,9 @@
// @ts-strict-ignore
import { Jsonify } from "type-fest";
import { OrganizationId } from "@bitwarden/common/types/guid";
import { EncryptService } from "../../platform/abstractions/encrypt.service";
import { EncString } from "../../platform/models/domain/enc-string";
import { OrganizationId } from "../../types/guid";
import { OrgKey } from "../../types/key";
import { DataPacker } from "../state/data-packer.abstraction";

View File

@@ -2,9 +2,8 @@
// @ts-strict-ignore
import { Jsonify } from "type-fest";
import { UserId } from "@bitwarden/common/types/guid";
import { EncString } from "../../platform/models/domain/enc-string";
import { UserId } from "../../types/guid";
/** An encryption strategy that protects a type's secrets with
* user-specific keys. This strategy is bound to a specific user.

View File

@@ -2,10 +2,9 @@
// @ts-strict-ignore
import { Jsonify } from "type-fest";
import { UserId } from "@bitwarden/common/types/guid";
import { EncryptService } from "../../platform/abstractions/encrypt.service";
import { EncString } from "../../platform/models/domain/enc-string";
import { UserId } from "../../types/guid";
import { UserKey } from "../../types/key";
import { DataPacker } from "../state/data-packer.abstraction";

View File

@@ -1,7 +1,7 @@
import { Observable } from "rxjs";
import { Policy } from "@bitwarden/common/admin-console/models/domain/policy";
import { OrganizationId, UserId } from "@bitwarden/common/types/guid";
import { Policy } from "../admin-console/models/domain/policy";
import { OrganizationId, UserId } from "../types/guid";
import { OrganizationEncryptor } from "./cryptography/organization-encryptor.abstraction";
import { UserEncryptor } from "./cryptography/user-encryptor.abstraction";

View File

@@ -1,6 +1,6 @@
import { mock } from "jest-mock-extended";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { I18nService } from "../../platform/abstractions/i18n.service";
import { IntegrationContext } from "./integration-context";
import { IntegrationId } from "./integration-id";

View File

@@ -1,7 +1,7 @@
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { I18nService } from "../../platform/abstractions/i18n.service";
import { Utils } from "../../platform/misc/utils";
import { IntegrationMetadata } from "./integration-metadata";
import { ApiSettings, IntegrationRequest } from "./rpc";

View File

@@ -1,7 +1,7 @@
import { mock } from "jest-mock-extended";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { ApiService } from "../../../abstractions/api.service";
import { I18nService } from "../../../platform/abstractions/i18n.service";
import { IntegrationRequest } from "./integration-request";
import { RestClient } from "./rest-client";

View File

@@ -1,7 +1,7 @@
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { ApiService } from "../../../abstractions/api.service";
import { I18nService } from "../../../platform/abstractions/i18n.service";
import { IntegrationRequest } from "./integration-request";
import { JsonRpc } from "./rpc";

View File

@@ -2,7 +2,7 @@
// @ts-strict-ignore
import { Jsonify } from "type-fest";
import { Classifier } from "@bitwarden/common/tools/state/classifier";
import { Classifier } from "./state/classifier";
export class PrivateClassifier<Data> implements Classifier<Data, Record<string, never>, Data> {
constructor(private keys: (keyof Jsonify<Data>)[] = undefined) {}

View File

@@ -2,7 +2,7 @@
// @ts-strict-ignore
import { Jsonify } from "type-fest";
import { Classifier } from "@bitwarden/common/tools/state/classifier";
import { Classifier } from "./state/classifier";
export class PublicClassifier<Data> implements Classifier<Data, Data, Record<string, never>> {
constructor(private keys: (keyof Jsonify<Data>)[]) {}

View File

@@ -1,12 +1,12 @@
import { mock } from "jest-mock-extended";
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
import { UserKey } from "@bitwarden/common/types/key";
import { KeyService } from "@bitwarden/key-management";
import { makeStaticByteArray, mockEnc } from "../../../../../spec";
import { EncryptService } from "../../../../platform/abstractions/encrypt.service";
import { SymmetricCryptoKey } from "../../../../platform/models/domain/symmetric-crypto-key";
import { ContainerService } from "../../../../platform/services/container.service";
import { UserKey } from "../../../../types/key";
import { SendType } from "../../enums/send-type";
import { SendData } from "../data/send.data";

View File

@@ -1,8 +1,6 @@
import { mock } from "jest-mock-extended";
import { firstValueFrom, of } from "rxjs";
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
import { SelfHostedEnvironment } from "@bitwarden/common/platform/services/default-environment.service";
import { KeyService } from "@bitwarden/key-management";
import {
@@ -13,12 +11,14 @@ import {
mockAccountServiceWith,
} from "../../../../spec";
import { EncryptService } from "../../../platform/abstractions/encrypt.service";
import { EnvironmentService } from "../../../platform/abstractions/environment.service";
import { I18nService } from "../../../platform/abstractions/i18n.service";
import { KeyGenerationService } from "../../../platform/abstractions/key-generation.service";
import { Utils } from "../../../platform/misc/utils";
import { EncString } from "../../../platform/models/domain/enc-string";
import { SymmetricCryptoKey } from "../../../platform/models/domain/symmetric-crypto-key";
import { ContainerService } from "../../../platform/services/container.service";
import { SelfHostedEnvironment } from "../../../platform/services/default-environment.service";
import { UserId } from "../../../types/guid";
import { UserKey } from "../../../types/key";
import { SendType } from "../enums/send-type";

View File

@@ -1,9 +1,8 @@
import { BehaviorSubject, of, Subject } from "rxjs";
import { GENERATOR_DISK, UserKeyDefinition } from "@bitwarden/common/platform/state";
import { UserId } from "@bitwarden/common/types/guid";
import { awaitAsync, FakeSingleUserState, ObservableTracker } from "../../../spec";
import { GENERATOR_DISK, UserKeyDefinition } from "../../platform/state";
import { UserId } from "../../types/guid";
import { UserEncryptor } from "../cryptography/user-encryptor.abstraction";
import { UserBound } from "../dependencies";
import { PrivateClassifier } from "../private-classifier";

View File

@@ -26,10 +26,9 @@ import {
skip,
} from "rxjs";
import { EncString } from "@bitwarden/common/platform/models/domain/enc-string";
import { SingleUserState, UserKeyDefinition } from "@bitwarden/common/platform/state";
import { UserId } from "@bitwarden/common/types/guid";
import { EncString } from "../../platform/models/domain/enc-string";
import { SingleUserState, UserKeyDefinition } from "../../platform/state";
import { UserId } from "../../types/guid";
import { UserEncryptor } from "../cryptography/user-encryptor.abstraction";
import { UserBound } from "../dependencies";
import { anyComplete, errorOnChange, ready, withLatestReady } from "../rx";

View File

@@ -2,7 +2,6 @@
// @ts-strict-ignore
import { Observable } from "rxjs";
import { LocalData } from "@bitwarden/common/vault/models/data/local.data";
import { UserKeyRotationDataProvider } from "@bitwarden/key-management";
import { UriMatchStrategySetting } from "../../models/domain/domain-service";
@@ -11,6 +10,7 @@ import { CipherId, CollectionId, OrganizationId, UserId } from "../../types/guid
import { UserKey } from "../../types/key";
import { CipherType } from "../enums/cipher-type";
import { CipherData } from "../models/data/cipher.data";
import { LocalData } from "../models/data/local.data";
import { Cipher } from "../models/domain/cipher";
import { Field } from "../models/domain/field";
import { CipherWithIdRequest } from "../models/request/cipher-with-id.request";

Some files were not shown because too many files have changed in this diff Show More