1
0
mirror of https://github.com/bitwarden/browser.git synced 2025-01-06 18:57:56 +01:00

[PM-2998] Move Approving Device Check (#5822)

* Switch to retrieving approving device from token response

- Remove exist-by-types API call
- Define `HasApprovingDevices` on TDE options

* Update Naming

* Update Test

* Update Missing Names
This commit is contained in:
Justin Baur 2023-07-17 08:56:30 -04:00 committed by GitHub
parent 0696252fb3
commit c85ddfe833
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 14 additions and 58 deletions

View File

@ -7,7 +7,6 @@ import {
switchMap,
Subject,
catchError,
forkJoin,
from,
of,
finalize,
@ -22,11 +21,6 @@ import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-conso
import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction";
import { LoginService } from "@bitwarden/common/auth/abstractions/login.service";
import { TokenService } from "@bitwarden/common/auth/abstractions/token.service";
import {
DesktopDeviceTypes,
DeviceType,
MobileDeviceTypes,
} from "@bitwarden/common/enums/device-type.enum";
import { KeysRequest } from "@bitwarden/common/models/request/keys.request";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
@ -170,23 +164,6 @@ export class BaseLoginDecryptionOptionsComponent implements OnInit, OnDestroy {
loadUntrustedDeviceData(accountDecryptionOptions: AccountDecryptionOptions) {
this.loading = true;
const mobileAndDesktopDeviceTypes: DeviceType[] = Array.from(MobileDeviceTypes).concat(
Array.from(DesktopDeviceTypes)
);
// Note: Each obs must handle error here and protect inner observable b/c we are using forkJoin below
// as per RxJs docs: if any given observable errors at some point, then
// forkJoin will error as well and immediately unsubscribe from the other observables.
const mobileOrDesktopDevicesExistence$ = this.devicesService
.getDevicesExistenceByTypes$(mobileAndDesktopDeviceTypes)
.pipe(
catchError((err: unknown) => {
this.validationService.showError(err);
return of(undefined);
}),
takeUntil(this.destroy$)
);
const email$ = from(this.stateService.getEmail()).pipe(
catchError((err: unknown) => {
this.validationService.showError(err);
@ -195,18 +172,16 @@ export class BaseLoginDecryptionOptionsComponent implements OnInit, OnDestroy {
takeUntil(this.destroy$)
);
forkJoin({
mobileOrDesktopDevicesExistence: mobileOrDesktopDevicesExistence$,
email: email$,
})
email$
.pipe(
takeUntil(this.destroy$),
finalize(() => {
this.loading = false;
})
)
.subscribe(({ mobileOrDesktopDevicesExistence, email }) => {
const showApproveFromOtherDeviceBtn = mobileOrDesktopDevicesExistence || false;
.subscribe((email) => {
const showApproveFromOtherDeviceBtn =
accountDecryptionOptions?.trustedDeviceOption?.hasLoginApprovingDevice || false;
const showReqAdminApprovalBtn =
!!accountDecryptionOptions?.trustedDeviceOption?.hasAdminApproval || false;

View File

@ -1,4 +1,3 @@
import { DeviceType } from "../../enums";
import { ListResponse } from "../../models/response/list.response";
import { DeviceResponse } from "./responses/device.response";
@ -9,7 +8,6 @@ export abstract class DevicesApiServiceAbstraction {
getDeviceByIdentifier: (deviceIdentifier: string) => Promise<DeviceResponse>;
getDevices: () => Promise<ListResponse<DeviceResponse>>;
getDevicesExistenceByTypes: (deviceTypes: DeviceType[]) => Promise<boolean>;
updateTrustedDeviceKeys: (
deviceIdentifier: string,

View File

@ -1,12 +1,9 @@
import { Observable } from "rxjs";
import { DeviceType } from "../../enums";
import { DeviceView } from "./views/device.view";
export abstract class DevicesServiceAbstraction {
getDevices$: () => Observable<Array<DeviceView>>;
getDevicesExistenceByTypes$: (deviceTypes: DeviceType[]) => Observable<boolean>;
getDeviceByIdentifier$: (deviceIdentifier: string) => Observable<DeviceView>;
isDeviceKnownForUser$: (email: string, deviceIdentifier: string) => Observable<boolean>;
updateTrustedDeviceKeys$: (

View File

@ -148,6 +148,7 @@ describe("SsoLogInStrategy", () => {
HasMasterPassword: true,
TrustedDeviceOption: {
HasAdminApproval: true,
HasLoginApprovingDevice: true,
EncryptedPrivateKey: mockEncDevicePrivateKey,
EncryptedUserKey: mockEncUserKey,
},

View File

@ -1,3 +1,3 @@
export class TrustedDeviceUserDecryptionOption {
constructor(public hasAdminApproval: boolean) {}
constructor(public hasAdminApproval: boolean, public hasLoginApprovingDevice: boolean) {}
}

View File

@ -3,12 +3,14 @@ import { EncString } from "../../../../platform/models/domain/enc-string";
export interface ITrustedDeviceUserDecryptionOptionServerResponse {
HasAdminApproval: boolean;
HasLoginApprovingDevice: boolean;
EncryptedPrivateKey?: string;
EncryptedUserKey?: string;
}
export class TrustedDeviceUserDecryptionOptionResponse extends BaseResponse {
hasAdminApproval: boolean;
hasLoginApprovingDevice: boolean;
encryptedPrivateKey: EncString;
encryptedUserKey: EncString;
@ -16,6 +18,8 @@ export class TrustedDeviceUserDecryptionOptionResponse extends BaseResponse {
super(response);
this.hasAdminApproval = this.getResponseProperty("HasAdminApproval");
this.hasLoginApprovingDevice = this.getResponseProperty("HasLoginApprovingDevice");
if (response.EncryptedPrivateKey) {
this.encryptedPrivateKey = new EncString(this.getResponseProperty("EncryptedPrivateKey"));
}

View File

@ -322,7 +322,8 @@ export class AccountDecryptionOptions {
if (response.trustedDeviceOption) {
accountDecryptionOptions.trustedDeviceOption = new TrustedDeviceUserDecryptionOption(
response.trustedDeviceOption.hasAdminApproval
response.trustedDeviceOption.hasAdminApproval,
response.trustedDeviceOption.hasLoginApprovingDevice
);
}
@ -344,7 +345,8 @@ export class AccountDecryptionOptions {
if (obj.trustedDeviceOption) {
accountDecryptionOptions.trustedDeviceOption = new TrustedDeviceUserDecryptionOption(
obj.trustedDeviceOption.hasAdminApproval
obj.trustedDeviceOption.hasAdminApproval,
obj.trustedDeviceOption.hasLoginApprovingDevice
);
}

View File

@ -1,7 +1,6 @@
import { ApiService } from "../../abstractions/api.service";
import { DevicesApiServiceAbstraction } from "../../abstractions/devices/devices-api.service.abstraction";
import { DeviceResponse } from "../../abstractions/devices/responses/device.response";
import { DeviceType } from "../../enums";
import { ListResponse } from "../../models/response/list.response";
import { Utils } from "../../platform/misc/utils";
@ -46,18 +45,6 @@ export class DevicesApiServiceImplementation implements DevicesApiServiceAbstrac
return new ListResponse(r, DeviceResponse);
}
async getDevicesExistenceByTypes(deviceTypes: DeviceType[]): Promise<boolean> {
const r = await this.apiService.send(
"POST",
"/devices/exist-by-types",
deviceTypes,
true,
true,
null
);
return Boolean(r);
}
async updateTrustedDeviceKeys(
deviceIdentifier: string,
devicePublicKeyEncryptedUserKey: string,

View File

@ -4,7 +4,6 @@ import { DevicesApiServiceAbstraction } from "../../abstractions/devices/devices
import { DevicesServiceAbstraction } from "../../abstractions/devices/devices.service.abstraction";
import { DeviceResponse } from "../../abstractions/devices/responses/device.response";
import { DeviceView } from "../../abstractions/devices/views/device.view";
import { DeviceType } from "../../enums";
import { ListResponse } from "../../models/response/list.response";
/**
@ -31,13 +30,6 @@ export class DevicesServiceImplementation implements DevicesServiceAbstraction {
);
}
/**
* @description Returns whether the user has any devices of the specified types.
*/
getDevicesExistenceByTypes$(deviceTypes: DeviceType[]): Observable<boolean> {
return defer(() => this.devicesApiService.getDevicesExistenceByTypes(deviceTypes));
}
/**
* @description Gets the device with the specified identifier.
*/