From 70b743347cf6527f5ae153b12c64a3a3c83aeb66 Mon Sep 17 00:00:00 2001 From: Thomas Rittson Date: Wed, 15 Dec 2021 10:40:57 +1000 Subject: [PATCH] Initial test for authService --- spec/common/services/auth.service.spec.ts | 177 ++++++++++++++++++++++ 1 file changed, 177 insertions(+) create mode 100644 spec/common/services/auth.service.spec.ts diff --git a/spec/common/services/auth.service.spec.ts b/spec/common/services/auth.service.spec.ts new file mode 100644 index 00000000..222cc4e1 --- /dev/null +++ b/spec/common/services/auth.service.spec.ts @@ -0,0 +1,177 @@ +import { Arg, Substitute, SubstituteOf } from '@fluffy-spoon/substitute'; + +import { ApiService } from 'jslib-common/abstractions/api.service'; +import { AppIdService } from 'jslib-common/abstractions/appId.service'; +import { CryptoFunctionService } from 'jslib-common/abstractions/cryptoFunction.service'; +import { CryptoService } from 'jslib-common/abstractions/crypto.service'; +import { EnvironmentService } from 'jslib-common/abstractions/environment.service' +import { I18nService } from 'jslib-common/abstractions/i18n.service'; +import { KeyConnectorService } from 'jslib-common/abstractions/keyConnector.service' +import { LogService } from 'jslib-common/abstractions/log.service'; +import { MessagingService } from 'jslib-common/abstractions/messaging.service'; +import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service'; +import { TokenService } from 'jslib-common/abstractions/token.service'; +import { VaultTimeoutService } from 'jslib-common/abstractions/vaultTimeout.service' + +import { AuthService } from 'jslib-common/services/auth.service'; + +import { Utils } from 'jslib-common/misc/utils'; +import { SymmetricCryptoKey } from 'jslib-common/models/domain/symmetricCryptoKey'; +import { HashPurpose } from 'jslib-common/enums/hashPurpose'; +import { AuthResult } from 'jslib-common/models/domain/authResult'; +import { IdentityTokenResponse } from 'jslib-common/models/response/identityTokenResponse'; +import { StateService } from 'jslib-common/abstractions/state.service'; +import { access } from 'fs'; +import { AccountProfile, AccountTokens } from 'jslib-common/models/domain/account'; + +describe('Cipher Service', () => { + let cryptoService: SubstituteOf; + let apiService: SubstituteOf; + let tokenService: SubstituteOf; + let appIdService: SubstituteOf; + let i18nService: SubstituteOf; + let platformUtilsService: SubstituteOf; + let messagingService: SubstituteOf; + let vaultTimeoutService: SubstituteOf; + let logService: SubstituteOf; + let cryptoFunctionService: SubstituteOf; + let environmentService: SubstituteOf; + let keyConnectorService: SubstituteOf; + let stateService: SubstituteOf; + let setCryptoKeys = true; + + const email = 'hello@world.com'; + const masterPassword = 'password'; + const hashedPassword = 'HASHED_PASSWORD'; + const localHashedPassword = 'LOCAL_HASHED_PASSWORD'; + const preloginKey = new SymmetricCryptoKey(Utils.fromB64ToArray('XVs4Gg+EdUXb9mHsSA6iOa5e88iVLvUtP/L0OXIamVA=')); + const deviceId = Utils.newGuid(); + + let authService: AuthService; + + beforeEach(() => { + cryptoService = Substitute.for(); + apiService = Substitute.for(); + tokenService = Substitute.for(); + appIdService = Substitute.for(); + i18nService = Substitute.for(); + platformUtilsService = Substitute.for(); + messagingService = Substitute.for(); + vaultTimeoutService = Substitute.for(); + logService = Substitute.for(); + cryptoFunctionService = Substitute.for(); + environmentService = Substitute.for(); + stateService = Substitute.for(); + keyConnectorService = Substitute.for(); + + authService = new AuthService(cryptoService, apiService, tokenService, appIdService, i18nService, + platformUtilsService, messagingService, vaultTimeoutService, logService, cryptoFunctionService, + keyConnectorService, environmentService, stateService, setCryptoKeys); + authService.init(); + }); + + it('logIn method: simple call: no 2FA, captcha or password reset', async () => { + // Arrange - entry method + cryptoService.makeKey(masterPassword, email, Arg.any(), Arg.any()).resolves(preloginKey); + cryptoService.hashPassword(masterPassword, Arg.any()).resolves(hashedPassword); + cryptoService.hashPassword(masterPassword, Arg.any(), HashPurpose.LocalAuthorization).resolves(localHashedPassword); + // captchaToken is null + + // Arrange - logInHelper + tokenService.getTwoFactorToken(email).resolves(null); + appIdService.getAppId().resolves(deviceId); + + // Arrange tokenResponse + const tokenResponse = new IdentityTokenResponse({}); + (tokenResponse as any).twoFactorProviders2 = false; + (tokenResponse as any).siteKey = undefined; + tokenResponse.resetMasterPassword = false; + tokenResponse.forcePasswordReset = false; + + const accessToken = 'ACCESS_TOKEN'; + tokenResponse.accessToken = accessToken; + + const refreshToken = 'REFRESH_TOKEN'; + tokenResponse.refreshToken = refreshToken; + + const kdf = 0; + const kdfIterations = 10000; + tokenResponse.kdf = kdf; + tokenResponse.kdfIterations = kdfIterations; + + const encKey = 'ENC_KEY'; + tokenResponse.key = encKey; + + const userId = Utils.newGuid(); + const decodedToken = { + sub: userId, + email: email, + premium: false, + } + tokenService.decodeToken(accessToken).resolves(decodedToken) + + const privateKey = 'PRIVATE_KEY'; + tokenResponse.privateKey = privateKey; + + apiService.postIdentityToken(Arg.any()).resolves(tokenResponse); + + const expected = new AuthResult(); + expected.forcePasswordReset = false; + expected.resetMasterPassword = false; + expected.twoFactor = false; + expected.twoFactorProviders = null; + expected.captchaSiteKey = undefined; + + // Act + const result = await authService.logIn(email, masterPassword); + + // Assert + // Api call: + apiService.received(1).postIdentityToken(Arg.is(actual => + actual.email === email && + actual.masterPasswordHash === hashedPassword && + actual.device.identifier === deviceId && + actual.provider == null && + actual.token == null && + actual.captchaResponse == null)); + + // Sets local environment: + stateService.received(1).addAccount({ + profile: { + ...new AccountProfile(), + ...{ + userId: userId, + email: email, + apiKeyClientId: null, + apiKeyClientSecret: null, + hasPremiumPersonally: false, + kdfIterations: tokenResponse.kdfIterations, + kdfType: tokenResponse.kdf, + }, + }, + tokens: { + ...new AccountTokens(), + ...{ + accessToken: tokenResponse.accessToken, + refreshToken: tokenResponse.refreshToken, + }, + }, + }) + cryptoService.received(1).setKey(preloginKey); + cryptoService.received(1).setKeyHash(localHashedPassword); + cryptoService.received(1).setEncKey(encKey); + cryptoService.received(1).setEncPrivateKey(privateKey); + stateService.received(1).setBiometricLocked(false); + + messagingService.received(1).send('loggedIn'); + + // Negative tests + apiService.didNotReceive().postAccountKeys(Arg.any()); // Did not generate new private key pair + keyConnectorService.didNotReceive().getAndSetKey(Arg.any()); // Did not fetch Key Connector key + apiService.didNotReceive().postUserKeyToKeyConnector(Arg.any(), Arg.any()); // Did not send key to KC + tokenService.didNotReceive().setTwoFactorToken(Arg.any(), Arg.any()); // Did not save 2FA token + + // Return result: + expect(result).toEqual(expected); + }); +});