mirror of
https://github.com/bitwarden/browser
synced 2025-12-17 16:53:34 +00:00
[PM-5404, PM-3518] Migrate user decryption options to new service (#7344)
* create new user decryption options service * rename new service to user decryption options * add hasMasterPassword to user decryption options service * migrate device trust service to new user decryption options service * add migration for user-decryption-options * migrate sync service and calls to trust-device-service * rename abstraction file * migrate two factor component * migrate two factor spec * migrate sso component * migrate set-password component * migrate base login decryption component * migrate organization options component * fix component imports * add missing imports - remove state service calls - add update user decryption options method * remove acct decryption options from account * lint * fix tests and linting * fix browser * fix desktop * add user decryption options service to cli * remove default value from migration * bump migration number * fix merge conflict * fix vault timeout settings * fix cli * more fixes * add user decryption options service to deps of vault timeout settings service * update login strategy service with user decryption options * remove early return from sync bandaid for user decryption options * move user decryption options service to lib/auth * move user decryption options to libs/auth * fix reference * fix browser * check user decryption options after 2fa check * update migration and revert tsconfig changes * add more documentation * clear user decryption options on logout * fix tests by creating helper for user decryption options * fix tests * pr feedback * fix factory * update migration * add tests * update missed migration num in test
This commit is contained in:
@@ -1,2 +1,3 @@
|
||||
export * from "./rotateable-key-set";
|
||||
export * from "./login-credentials";
|
||||
export * from "./user-decryption-options";
|
||||
|
||||
153
libs/auth/src/common/models/domain/user-decryption-options.ts
Normal file
153
libs/auth/src/common/models/domain/user-decryption-options.ts
Normal file
@@ -0,0 +1,153 @@
|
||||
import { Jsonify } from "type-fest";
|
||||
|
||||
import { KeyConnectorUserDecryptionOptionResponse } from "@bitwarden/common/auth/models/response/user-decryption-options/key-connector-user-decryption-option.response";
|
||||
import { TrustedDeviceUserDecryptionOptionResponse } from "@bitwarden/common/auth/models/response/user-decryption-options/trusted-device-user-decryption-option.response";
|
||||
import { IdentityTokenResponse } from "@bitwarden/common/src/auth/models/response/identity-token.response";
|
||||
|
||||
/**
|
||||
* Key Connector decryption options. Intended to be sent to the client for use after authentication.
|
||||
* @see {@link UserDecryptionOptions}
|
||||
*/
|
||||
export class KeyConnectorUserDecryptionOption {
|
||||
/** The URL of the key connector configured for this user. */
|
||||
keyConnectorUrl: string;
|
||||
|
||||
/**
|
||||
* Initializes a new instance of the KeyConnectorUserDecryptionOption from a response object.
|
||||
* @param response The key connector user decryption option response object.
|
||||
* @returns A new instance of the KeyConnectorUserDecryptionOption. Will initialize even if the response is nullish.
|
||||
*/
|
||||
static fromResponse(
|
||||
response: KeyConnectorUserDecryptionOptionResponse,
|
||||
): KeyConnectorUserDecryptionOption {
|
||||
const options = new KeyConnectorUserDecryptionOption();
|
||||
options.keyConnectorUrl = response?.keyConnectorUrl ?? null;
|
||||
return options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes a new instance of a KeyConnectorUserDecryptionOption from a JSON object.
|
||||
* @param obj JSON object to deserialize.
|
||||
* @returns A new instance of the KeyConnectorUserDecryptionOption. Will initialize even if the JSON object is nullish.
|
||||
*/
|
||||
static fromJSON(
|
||||
obj: Jsonify<KeyConnectorUserDecryptionOption>,
|
||||
): KeyConnectorUserDecryptionOption {
|
||||
return Object.assign(new KeyConnectorUserDecryptionOption(), obj);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Trusted device decryption options. Intended to be sent to the client for use after authentication.
|
||||
* @see {@link UserDecryptionOptions}
|
||||
*/
|
||||
export class TrustedDeviceUserDecryptionOption {
|
||||
/** True if an admin has approved an admin auth request previously made from this device. */
|
||||
hasAdminApproval: boolean;
|
||||
/** True if the user has a device capable of approving an auth request. */
|
||||
hasLoginApprovingDevice: boolean;
|
||||
/** True if the user has manage reset password permission, as these users must be forced to have a master password. */
|
||||
hasManageResetPasswordPermission: boolean;
|
||||
|
||||
/**
|
||||
* Initializes a new instance of the TrustedDeviceUserDecryptionOption from a response object.
|
||||
* @param response The trusted device user decryption option response object.
|
||||
* @returns A new instance of the TrustedDeviceUserDecryptionOption. Will initialize even if the response is nullish.
|
||||
*/
|
||||
static fromResponse(
|
||||
response: TrustedDeviceUserDecryptionOptionResponse,
|
||||
): TrustedDeviceUserDecryptionOption {
|
||||
const options = new TrustedDeviceUserDecryptionOption();
|
||||
options.hasAdminApproval = response?.hasAdminApproval ?? false;
|
||||
options.hasLoginApprovingDevice = response?.hasLoginApprovingDevice ?? false;
|
||||
options.hasManageResetPasswordPermission = response?.hasManageResetPasswordPermission ?? false;
|
||||
return options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes a new instance of the TrustedDeviceUserDecryptionOption from a JSON object.
|
||||
* @param obj JSON object to deserialize.
|
||||
* @returns A new instance of the TrustedDeviceUserDecryptionOption. Will initialize even if the JSON object is nullish.
|
||||
*/
|
||||
static fromJSON(
|
||||
obj: Jsonify<TrustedDeviceUserDecryptionOption>,
|
||||
): TrustedDeviceUserDecryptionOption {
|
||||
return Object.assign(new TrustedDeviceUserDecryptionOption(), obj);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the decryption options the user has configured on the server. This is intended to be sent
|
||||
* to the client on authentication, and can be used to determine how to decrypt the user's vault.
|
||||
*/
|
||||
export class UserDecryptionOptions {
|
||||
/** True if the user has a master password configured on the server. */
|
||||
hasMasterPassword: boolean;
|
||||
/** {@link TrustedDeviceUserDecryptionOption} */
|
||||
trustedDeviceOption?: TrustedDeviceUserDecryptionOption;
|
||||
/** {@link KeyConnectorUserDecryptionOption} */
|
||||
keyConnectorOption?: KeyConnectorUserDecryptionOption;
|
||||
|
||||
/**
|
||||
* Initializes a new instance of the UserDecryptionOptions from a response object.
|
||||
* @param response user decryption options response object
|
||||
* @returns A new instance of the UserDecryptionOptions.
|
||||
* @throws If the response is nullish, this method will throw an error. User decryption options
|
||||
* are required for client initialization.
|
||||
*/
|
||||
// TODO: Change response type to `UserDecryptionOptionsResponse` after 2023.10 release (https://bitwarden.atlassian.net/browse/PM-3537)
|
||||
static fromResponse(response: IdentityTokenResponse): UserDecryptionOptions {
|
||||
if (response == null) {
|
||||
throw new Error("User Decryption Options are required for client initialization.");
|
||||
}
|
||||
|
||||
const decryptionOptions = new UserDecryptionOptions();
|
||||
|
||||
if (response.userDecryptionOptions) {
|
||||
// If the response has userDecryptionOptions, this means it's on a post-TDE server version and can interrogate
|
||||
// the new decryption options.
|
||||
const responseOptions = response.userDecryptionOptions;
|
||||
decryptionOptions.hasMasterPassword = responseOptions.hasMasterPassword;
|
||||
|
||||
decryptionOptions.trustedDeviceOption = TrustedDeviceUserDecryptionOption.fromResponse(
|
||||
responseOptions.trustedDeviceOption,
|
||||
);
|
||||
|
||||
decryptionOptions.keyConnectorOption = KeyConnectorUserDecryptionOption.fromResponse(
|
||||
responseOptions.keyConnectorOption,
|
||||
);
|
||||
} else {
|
||||
// If the response does not have userDecryptionOptions, this means it's on a pre-TDE server version and so
|
||||
// we must base our decryption options on the presence of the keyConnectorUrl.
|
||||
// Note that the presence of keyConnectorUrl implies that the user does not have a master password, as in pre-TDE
|
||||
// server versions, a master password short-circuited the addition of the keyConnectorUrl to the response.
|
||||
// TODO: remove this check after 2023.10 release (https://bitwarden.atlassian.net/browse/PM-3537)
|
||||
const usingKeyConnector = response.keyConnectorUrl != null;
|
||||
decryptionOptions.hasMasterPassword = !usingKeyConnector;
|
||||
if (usingKeyConnector) {
|
||||
decryptionOptions.keyConnectorOption = new KeyConnectorUserDecryptionOption();
|
||||
decryptionOptions.keyConnectorOption.keyConnectorUrl = response.keyConnectorUrl;
|
||||
}
|
||||
}
|
||||
return decryptionOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes a new instance of the UserDecryptionOptions from a JSON object.
|
||||
* @param obj JSON object to deserialize.
|
||||
* @returns A new instance of the UserDecryptionOptions. Will initialize even if the JSON object is nullish.
|
||||
*/
|
||||
static fromJSON(obj: Jsonify<UserDecryptionOptions>): UserDecryptionOptions {
|
||||
const decryptionOptions = Object.assign(new UserDecryptionOptions(), obj);
|
||||
|
||||
decryptionOptions.trustedDeviceOption = TrustedDeviceUserDecryptionOption.fromJSON(
|
||||
obj?.trustedDeviceOption,
|
||||
);
|
||||
|
||||
decryptionOptions.keyConnectorOption = KeyConnectorUserDecryptionOption.fromJSON(
|
||||
obj?.keyConnectorOption,
|
||||
);
|
||||
|
||||
return decryptionOptions;
|
||||
}
|
||||
}
|
||||
@@ -1 +1,2 @@
|
||||
export * from "./domain";
|
||||
export * from "./spec";
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
import {
|
||||
KeyConnectorUserDecryptionOption,
|
||||
TrustedDeviceUserDecryptionOption,
|
||||
UserDecryptionOptions,
|
||||
} from "../domain";
|
||||
|
||||
// To discourage creating new user decryption options, we don't expose a constructor.
|
||||
// These helpers are for testing purposes only.
|
||||
|
||||
/** Testing helper for creating new instances of `UserDecryptionOptions` */
|
||||
export class FakeUserDecryptionOptions extends UserDecryptionOptions {
|
||||
constructor(init: Partial<UserDecryptionOptions>) {
|
||||
super();
|
||||
Object.assign(this, init);
|
||||
}
|
||||
}
|
||||
|
||||
/** Testing helper for creating new instances of `KeyConnectorUserDecryptionOption` */
|
||||
export class FakeKeyConnectorUserDecryptionOption extends KeyConnectorUserDecryptionOption {
|
||||
constructor(keyConnectorUrl: string) {
|
||||
super();
|
||||
this.keyConnectorUrl = keyConnectorUrl;
|
||||
}
|
||||
}
|
||||
|
||||
/** Testing helper for creating new instances of `TrustedDeviceUserDecryptionOption` */
|
||||
export class FakeTrustedDeviceUserDecryptionOption extends TrustedDeviceUserDecryptionOption {
|
||||
constructor(
|
||||
hasAdminApproval: boolean,
|
||||
hasLoginApprovingDevice: boolean,
|
||||
hasManageResetPasswordPermission: boolean,
|
||||
) {
|
||||
super();
|
||||
this.hasAdminApproval = hasAdminApproval;
|
||||
this.hasLoginApprovingDevice = hasLoginApprovingDevice;
|
||||
this.hasManageResetPasswordPermission = hasManageResetPasswordPermission;
|
||||
}
|
||||
}
|
||||
1
libs/auth/src/common/models/spec/index.ts
Normal file
1
libs/auth/src/common/models/spec/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from "./fake-user-decryption-options";
|
||||
Reference in New Issue
Block a user