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:
3
.github/workflows/scan.yml
vendored
3
.github/workflows/scan.yml
vendored
@@ -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 }}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 () => {
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.",
|
||||
|
||||
@@ -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.",
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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>
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
4
apps/desktop/src/package-lock.json
generated
4
apps/desktop/src/package-lock.json
generated
@@ -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"
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)"
|
||||
|
||||
@@ -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"
|
||||
},
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>(),
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
/**
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 [
|
||||
|
||||
@@ -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}`;
|
||||
|
||||
@@ -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[]>;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import {
|
||||
BillingInvoiceResponse,
|
||||
BillingTransactionResponse,
|
||||
} from "@bitwarden/common/billing/models/response/billing.response";
|
||||
} from "../../models/response/billing.response";
|
||||
|
||||
export class OrganizationBillingApiServiceAbstraction {
|
||||
getBillingInvoices: (
|
||||
|
||||
@@ -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[];
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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";
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { PlanType } from "@bitwarden/common/billing/enums";
|
||||
import { PlanType } from "../../enums";
|
||||
|
||||
export class PreviewOrganizationInvoiceRequest {
|
||||
organizationId?: string;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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[] = [];
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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[] = [];
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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";
|
||||
|
||||
|
||||
@@ -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";
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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";
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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";
|
||||
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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) {}
|
||||
|
||||
@@ -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>)[]) {}
|
||||
|
||||
@@ -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";
|
||||
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user