mirror of
https://github.com/bitwarden/browser
synced 2025-12-11 13:53:34 +00:00
* feat(change-password-component): Change Password Update [18720] - Very close to complete. * fix(policy-enforcement): [PM-21085] Fix Bug with Policy Enforcement - Removed temp code to force the state I need to verify correctness. * fix(policy-enforcement): [PM-21085] Fix Bug with Policy Enforcement - Recover account working with change password component. * fix(policy-enforcement): [PM-21085] Fix Bug with Policy Enforcement - Made code more dry. * fix(change-password-component): Change Password Update [18720] - Updates to routing and the extension. Extension is still a wip. * fix(change-password-component): Change Password Update [18720] - Extension routing changes. * feat(change-password-component): Change Password Update [18720] - More extension work * feat(change-password-component): Change Password Update [18720] - Pausing work for now while we wait for product to hear back. * feat(change-password-component): Change Password Update [18720] - Removed duplicated anon layouts. * feat(change-password-component): Change Password Update [18720] - Tidied up code. * feat(change-password-component): Change Password Update [18720] - Small fixes to the styling * feat(change-password-component): Change Password Update [18720] - Adding more content for the routing. * feat(change-password-component): Change Password Update [18720] - Removed circular loop for now. * feat(change-password-component): Change Password Update [18720] - Made comments regarding the change password routing complexities with change-password and auth guard. * feat(change-password-component): Change Password Update [18720] - Undid some changes because they will be conflicts later on. * feat(change-password-component): Change Password Update [18720] - Small directive change. * feat(change-password-component): Change Password Update [18720] - Small changes and added some clarification on where I'm blocked * feat(change-password-component): Change Password Update [18720] - Org invite is seemingly working, found one bug to iron out. * refactor(change-password-component): Change Password Update [18720] - Fixed up policy service to be made more clear. * docs(change-password-component): Change Password Update [18720] - Updated documentation. * refactor(change-password-component): Change Password Update [18720] - Routing changes and policy service changes. * fix(change-password-component): Change Password Update [18720] - Wrapping up changes. * feat(change-password-component): Change Password Update [18720] - Should be working fully * feat(change-password-component): Change Password Update [18720] - Found a bug, working on password policy being present on login. * feat(change-password-component): Change Password Update [18720] - Turned on auth guard on other clients for change-password route. * feat(change-password-component): Change Password Update [18720] - Committing intermediate changes. * feat(change-password-component): Change Password Update [18720] - The master password policy endpoint has been added! Should be working. Testing now. * feat(change-password-component): Change Password Update [18720] - Minor fixes. * feat(change-password-component): Change Password Update [18720] - Undid naming change. * feat(change-password-component): Change Password Update [18720] - Removed comment. * feat(change-password-component): Change Password Update [18720] - Removed unneeded code. * fix(change-password-component): Change Password Update [18720] - Took org invite state out of service and made it accessible. * fix(change-password-component): Change Password Update [18720] - Small changes. * fix(change-password-component): Change Password Update [18720] - Split up org invite service into client specific implementations and have them injected into clients properly * feat(change-password-component): Change Password Update [18720] - Stopping work and going to switch to a new branch to pare down some of the solutions that were made to get this over the finish line * feat(change-password-component): Change Password Update [18720] - Started to remove functionality in the login.component and the password login strategy. * feat(change-password-component): Change Password Update [18720] - Removed more unneded changes. * feat(change-password-component): Change Password Update [18720] - Change password clearing state working properly. * fix(change-password-component): Change Password Update [18720] - Added docs and moved web implementation. * comments(change-password-component): Change Password Update [18720] - Added more notes. * test(change-password-component): Change Password Update [18720] - Added in tests for policy service. * comment(change-password-component): Change Password Update [18720] - Updated doc with correct ticket number. * comment(change-password-component): Change Password Update [18720] - Fixed doc. * test(change-password-component): Change Password Update [18720] - Fixed tests. * test(change-password-component): Change Password Update [18720] - Fixed linting errors. Have more tests to fix. * test(change-password-component): Change Password Update [18720] - Added back in ignore for typesafety. * fix(change-password-component): Change Password Update [18720] - Fixed other type issues. * test(change-password-component): Change Password Update [18720] - Fixed tests. * test(change-password-component): Change Password Update [18720] - Fixed more tests. * test(change-password-component): Change Password Update [18720] - Fixed tiny duplicate code. * fix(change-password-component): Change Password Update [18720] - Fixed desktop component. * fix(change-password-component): Change Password Update [18720] - Removed unused code * fix(change-password-component): Change Password Update [18720] - Fixed locales. * fix(change-password-component): Change Password Update [18720] - Removed tracing. * fix(change-password-component): Change Password Update [18720] - Removed duplicative services module entry. * fix(change-password-component): Change Password Update [18720] - Added comment. * fix(change-password-component): Change Password Update [18720] - Fixed unneeded call in two factor to get user id. * fix(change-password-component): Change Password Update [18720] - Fixed a couple of tiny things. * fix(change-password-component): Change Password Update [18720] - Added comment for later fix. * fix(change-password-component): Change Password Update [18720] - Fixed linting error. * PM-18720 - AuthGuard - move call to get isChangePasswordFlagOn down after other conditions for efficiency. * PM-18720 - PasswordLoginStrategy tests - test new feature flagged combine org invite policies logic for weak password evaluation. * PM-18720 - CLI - fix dep issue * PM-18720 - ChangePasswordComp - extract change password warning up out of input password component * PM-18720 - InputPassword - remove unused dependency. * PM-18720 - ChangePasswordComp - add callout dep * PM-18720 - Revert all anon-layout changes * PM-18720 - Anon Layout - finish reverting changes. * PM-18720 - WIP move of change password out of libs/auth * PM-18720 - Clean up remaining imports from moving change password out of libs/auth * PM-18720 - Add change-password barrel file for better import grouping * PM-18720 - Change Password comp - restore maxWidth * PM-18720 - After merge, fix errors * PM-18720 - Desktop - fix api service import * PM-18720 - NDV - fix routing. * PM-18720 - Change Password Comp - add logout service todo * PM-18720 - PasswordSettings - per feedback, component is already feature flagged behind PM16117_ChangeExistingPasswordRefactor so we can just delete the replaced callout (new text is in change-password comp) * PM-18720 - Routing Modules - properly flag new component behind feature flag. * PM-18720 - SSO Login Strategy - fix config service import since it is now in shared deps from main merge. * PM-18720 - Fix SSO login strategy tests * PM-18720 - Default Policy Service - address AC PR feedback --------- Co-authored-by: Jared Snider <jsnider@bitwarden.com> Co-authored-by: Jared Snider <116684653+JaredSnider-Bitwarden@users.noreply.github.com>
230 lines
9.3 KiB
TypeScript
230 lines
9.3 KiB
TypeScript
import { mock, MockProxy } from "jest-mock-extended";
|
|
import { BehaviorSubject } from "rxjs";
|
|
|
|
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
|
import { TokenService } from "@bitwarden/common/auth/abstractions/token.service";
|
|
import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service";
|
|
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
|
|
import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service";
|
|
import { KeyConnectorService } from "@bitwarden/common/key-management/key-connector/abstractions/key-connector.service";
|
|
import { FakeMasterPasswordService } from "@bitwarden/common/key-management/master-password/services/fake-master-password.service";
|
|
import {
|
|
VaultTimeoutAction,
|
|
VaultTimeoutSettingsService,
|
|
} from "@bitwarden/common/key-management/vault-timeout";
|
|
import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service";
|
|
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
|
import {
|
|
Environment,
|
|
EnvironmentService,
|
|
} from "@bitwarden/common/platform/abstractions/environment.service";
|
|
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
|
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
|
|
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
|
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
|
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
|
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
|
|
import { FakeAccountService, mockAccountServiceWith } from "@bitwarden/common/spec";
|
|
import { CsprngArray } from "@bitwarden/common/types/csprng";
|
|
import { UserId } from "@bitwarden/common/types/guid";
|
|
import { UserKey, MasterKey } from "@bitwarden/common/types/key";
|
|
import { KdfConfigService, KeyService } from "@bitwarden/key-management";
|
|
|
|
import { InternalUserDecryptionOptionsServiceAbstraction } from "../abstractions/user-decryption-options.service.abstraction";
|
|
import { UserApiLoginCredentials } from "../models/domain/login-credentials";
|
|
|
|
import { identityTokenResponseFactory } from "./login.strategy.spec";
|
|
import { UserApiLoginStrategy, UserApiLoginStrategyData } from "./user-api-login.strategy";
|
|
|
|
describe("UserApiLoginStrategy", () => {
|
|
let cache: UserApiLoginStrategyData;
|
|
let accountService: FakeAccountService;
|
|
let masterPasswordService: FakeMasterPasswordService;
|
|
|
|
let keyService: MockProxy<KeyService>;
|
|
let encryptService: MockProxy<EncryptService>;
|
|
let apiService: MockProxy<ApiService>;
|
|
let tokenService: MockProxy<TokenService>;
|
|
let appIdService: MockProxy<AppIdService>;
|
|
let platformUtilsService: MockProxy<PlatformUtilsService>;
|
|
let messagingService: MockProxy<MessagingService>;
|
|
let logService: MockProxy<LogService>;
|
|
let stateService: MockProxy<StateService>;
|
|
let twoFactorService: MockProxy<TwoFactorService>;
|
|
let userDecryptionOptionsService: MockProxy<InternalUserDecryptionOptionsServiceAbstraction>;
|
|
let keyConnectorService: MockProxy<KeyConnectorService>;
|
|
let environmentService: MockProxy<EnvironmentService>;
|
|
let billingAccountProfileStateService: MockProxy<BillingAccountProfileStateService>;
|
|
let vaultTimeoutSettingsService: MockProxy<VaultTimeoutSettingsService>;
|
|
let kdfConfigService: MockProxy<KdfConfigService>;
|
|
let configService: MockProxy<ConfigService>;
|
|
|
|
let apiLogInStrategy: UserApiLoginStrategy;
|
|
let credentials: UserApiLoginCredentials;
|
|
|
|
const mockVaultTimeoutAction = VaultTimeoutAction.Lock;
|
|
const mockVaultTimeout = 1000;
|
|
|
|
const userId = Utils.newGuid() as UserId;
|
|
const deviceId = Utils.newGuid();
|
|
const keyConnectorUrl = "KEY_CONNECTOR_URL";
|
|
const apiClientId = "API_CLIENT_ID";
|
|
const apiClientSecret = "API_CLIENT_SECRET";
|
|
|
|
beforeEach(async () => {
|
|
accountService = mockAccountServiceWith(userId);
|
|
masterPasswordService = new FakeMasterPasswordService();
|
|
|
|
keyService = mock<KeyService>();
|
|
apiService = mock<ApiService>();
|
|
tokenService = mock<TokenService>();
|
|
appIdService = mock<AppIdService>();
|
|
platformUtilsService = mock<PlatformUtilsService>();
|
|
messagingService = mock<MessagingService>();
|
|
logService = mock<LogService>();
|
|
stateService = mock<StateService>();
|
|
twoFactorService = mock<TwoFactorService>();
|
|
userDecryptionOptionsService = mock<InternalUserDecryptionOptionsServiceAbstraction>();
|
|
keyConnectorService = mock<KeyConnectorService>();
|
|
environmentService = mock<EnvironmentService>();
|
|
billingAccountProfileStateService = mock<BillingAccountProfileStateService>();
|
|
vaultTimeoutSettingsService = mock<VaultTimeoutSettingsService>();
|
|
kdfConfigService = mock<KdfConfigService>();
|
|
configService = mock<ConfigService>();
|
|
|
|
appIdService.getAppId.mockResolvedValue(deviceId);
|
|
tokenService.getTwoFactorToken.mockResolvedValue(null);
|
|
tokenService.decodeAccessToken.mockResolvedValue({
|
|
sub: userId,
|
|
});
|
|
|
|
apiLogInStrategy = new UserApiLoginStrategy(
|
|
cache,
|
|
keyConnectorService,
|
|
accountService,
|
|
masterPasswordService,
|
|
keyService,
|
|
encryptService,
|
|
apiService,
|
|
tokenService,
|
|
appIdService,
|
|
platformUtilsService,
|
|
messagingService,
|
|
logService,
|
|
stateService,
|
|
twoFactorService,
|
|
userDecryptionOptionsService,
|
|
billingAccountProfileStateService,
|
|
vaultTimeoutSettingsService,
|
|
kdfConfigService,
|
|
environmentService,
|
|
configService,
|
|
);
|
|
|
|
credentials = new UserApiLoginCredentials(apiClientId, apiClientSecret);
|
|
|
|
const mockVaultTimeoutActionBSub = new BehaviorSubject<VaultTimeoutAction>(
|
|
mockVaultTimeoutAction,
|
|
);
|
|
vaultTimeoutSettingsService.getVaultTimeoutActionByUserId$.mockReturnValue(
|
|
mockVaultTimeoutActionBSub.asObservable(),
|
|
);
|
|
|
|
const mockVaultTimeoutBSub = new BehaviorSubject<number>(mockVaultTimeout);
|
|
vaultTimeoutSettingsService.getVaultTimeoutByUserId$.mockReturnValue(
|
|
mockVaultTimeoutBSub.asObservable(),
|
|
);
|
|
});
|
|
|
|
it("sends api key credentials to the server", async () => {
|
|
apiService.postIdentityToken.mockResolvedValue(identityTokenResponseFactory());
|
|
await apiLogInStrategy.logIn(credentials);
|
|
|
|
expect(apiService.postIdentityToken).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
clientId: apiClientId,
|
|
clientSecret: apiClientSecret,
|
|
device: expect.objectContaining({
|
|
identifier: deviceId,
|
|
}),
|
|
twoFactor: expect.objectContaining({
|
|
provider: null,
|
|
token: null,
|
|
}),
|
|
}),
|
|
);
|
|
});
|
|
|
|
it("sets the local environment after a successful login", async () => {
|
|
apiService.postIdentityToken.mockResolvedValue(identityTokenResponseFactory());
|
|
|
|
await apiLogInStrategy.logIn(credentials);
|
|
|
|
expect(tokenService.setClientId).toHaveBeenCalledWith(
|
|
apiClientId,
|
|
mockVaultTimeoutAction,
|
|
mockVaultTimeout,
|
|
);
|
|
expect(tokenService.setClientSecret).toHaveBeenCalledWith(
|
|
apiClientSecret,
|
|
mockVaultTimeoutAction,
|
|
mockVaultTimeout,
|
|
);
|
|
expect(stateService.addAccount).toHaveBeenCalled();
|
|
});
|
|
|
|
it("sets the encrypted user key and private key from the identity token response", async () => {
|
|
const tokenResponse = identityTokenResponseFactory();
|
|
|
|
apiService.postIdentityToken.mockResolvedValue(tokenResponse);
|
|
|
|
await apiLogInStrategy.logIn(credentials);
|
|
|
|
expect(masterPasswordService.mock.setMasterKeyEncryptedUserKey).toHaveBeenCalledWith(
|
|
tokenResponse.key,
|
|
userId,
|
|
);
|
|
expect(keyService.setPrivateKey).toHaveBeenCalledWith(tokenResponse.privateKey, userId);
|
|
});
|
|
|
|
it("gets and sets the master key if Key Connector is enabled", async () => {
|
|
const tokenResponse = identityTokenResponseFactory();
|
|
tokenResponse.apiUseKeyConnector = true;
|
|
|
|
const env = mock<Environment>();
|
|
env.getKeyConnectorUrl.mockReturnValue(keyConnectorUrl);
|
|
environmentService.environment$ = new BehaviorSubject(env);
|
|
|
|
apiService.postIdentityToken.mockResolvedValue(tokenResponse);
|
|
|
|
await apiLogInStrategy.logIn(credentials);
|
|
|
|
expect(keyConnectorService.setMasterKeyFromUrl).toHaveBeenCalledWith(keyConnectorUrl, userId);
|
|
});
|
|
|
|
it("decrypts and sets the user key if Key Connector is enabled", async () => {
|
|
const userKey = new SymmetricCryptoKey(new Uint8Array(64).buffer as CsprngArray) as UserKey;
|
|
const masterKey = new SymmetricCryptoKey(new Uint8Array(64).buffer as CsprngArray) as MasterKey;
|
|
|
|
const tokenResponse = identityTokenResponseFactory();
|
|
tokenResponse.apiUseKeyConnector = true;
|
|
|
|
const env = mock<Environment>();
|
|
env.getKeyConnectorUrl.mockReturnValue(keyConnectorUrl);
|
|
environmentService.environment$ = new BehaviorSubject(env);
|
|
|
|
apiService.postIdentityToken.mockResolvedValue(tokenResponse);
|
|
masterPasswordService.masterKeySubject.next(masterKey);
|
|
masterPasswordService.mock.decryptUserKeyWithMasterKey.mockResolvedValue(userKey);
|
|
|
|
await apiLogInStrategy.logIn(credentials);
|
|
|
|
expect(masterPasswordService.mock.decryptUserKeyWithMasterKey).toHaveBeenCalledWith(
|
|
masterKey,
|
|
userId,
|
|
undefined,
|
|
);
|
|
expect(keyService.setUserKey).toHaveBeenCalledWith(userKey, userId);
|
|
});
|
|
});
|