mirror of
https://github.com/bitwarden/browser
synced 2025-12-16 00:03:56 +00:00
Merge branch 'master' into feature/org-admin-refresh
This commit is contained in:
@@ -9,7 +9,7 @@ import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
||||
import { LogService } from "@bitwarden/common/abstractions/log.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
|
||||
import { PolicyService } from "@bitwarden/common/abstractions/policy/policy.service.abstraction";
|
||||
import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification.service";
|
||||
import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification/userVerification.service.abstraction";
|
||||
import { EventType } from "@bitwarden/common/enums/eventType";
|
||||
import { PolicyType } from "@bitwarden/common/enums/policyType";
|
||||
|
||||
@@ -142,7 +142,7 @@ export class ExportComponent implements OnInit {
|
||||
return this.exportService.getFileName(prefix, extension);
|
||||
}
|
||||
|
||||
protected async collectEvent(): Promise<any> {
|
||||
protected async collectEvent(): Promise<void> {
|
||||
await this.eventService.collect(EventType.User_ClientExportedVault);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Directive, NgZone, OnInit } from "@angular/core";
|
||||
import { Directive, NgZone, OnDestroy, OnInit } from "@angular/core";
|
||||
import { Router } from "@angular/router";
|
||||
import { Subscription } from "rxjs";
|
||||
import { take } from "rxjs/operators";
|
||||
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
@@ -20,7 +21,7 @@ import { SymmetricCryptoKey } from "@bitwarden/common/models/domain/symmetricCry
|
||||
import { SecretVerificationRequest } from "@bitwarden/common/models/request/secretVerificationRequest";
|
||||
|
||||
@Directive()
|
||||
export class LockComponent implements OnInit {
|
||||
export class LockComponent implements OnInit, OnDestroy {
|
||||
masterPassword = "";
|
||||
pin = "";
|
||||
showPassword = false;
|
||||
@@ -39,6 +40,8 @@ export class LockComponent implements OnInit {
|
||||
private invalidPinAttempts = 0;
|
||||
private pinSet: [boolean, boolean];
|
||||
|
||||
private activeAccountSubscription: Subscription;
|
||||
|
||||
constructor(
|
||||
protected router: Router,
|
||||
protected i18nService: I18nService,
|
||||
@@ -57,11 +60,15 @@ export class LockComponent implements OnInit {
|
||||
async ngOnInit() {
|
||||
// Load the first and observe updates
|
||||
await this.load();
|
||||
this.stateService.activeAccount.subscribe(async () => {
|
||||
this.activeAccountSubscription = this.stateService.activeAccount$.subscribe(async () => {
|
||||
await this.load();
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.activeAccountSubscription.unsubscribe();
|
||||
}
|
||||
|
||||
async submit() {
|
||||
if (this.pinLock && (this.pin == null || this.pin === "")) {
|
||||
this.platformUtilsService.showToast(
|
||||
|
||||
@@ -10,7 +10,7 @@ import { PasswordGenerationService } from "@bitwarden/common/abstractions/passwo
|
||||
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
|
||||
import { PolicyService } from "@bitwarden/common/abstractions/policy/policy.service.abstraction";
|
||||
import { StateService } from "@bitwarden/common/abstractions/state.service";
|
||||
import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification.service";
|
||||
import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification/userVerification.service.abstraction";
|
||||
import { VerificationType } from "@bitwarden/common/enums/verificationType";
|
||||
import { EncString } from "@bitwarden/common/models/domain/encString";
|
||||
import { MasterPasswordPolicyOptions } from "@bitwarden/common/models/domain/masterPasswordPolicyOptions";
|
||||
@@ -28,7 +28,7 @@ export class UpdatePasswordComponent extends BaseChangePasswordComponent {
|
||||
showPassword = false;
|
||||
currentMasterPassword: string;
|
||||
|
||||
onSuccessfulChangePassword: () => Promise<any>;
|
||||
onSuccessfulChangePassword: () => Promise<void>;
|
||||
|
||||
constructor(
|
||||
protected router: Router,
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { animate, style, transition, trigger } from "@angular/animations";
|
||||
import { Component, OnInit } from "@angular/core";
|
||||
import { ControlValueAccessor, UntypedFormControl, NG_VALUE_ACCESSOR } from "@angular/forms";
|
||||
import { ControlValueAccessor, NG_VALUE_ACCESSOR, FormControl } from "@angular/forms";
|
||||
|
||||
import { KeyConnectorService } from "@bitwarden/common/abstractions/keyConnector.service";
|
||||
import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification.service";
|
||||
import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification/userVerification.service.abstraction";
|
||||
import { VerificationType } from "@bitwarden/common/enums/verificationType";
|
||||
import { Utils } from "@bitwarden/common/misc/utils";
|
||||
import { Verification } from "@bitwarden/common/types/verification";
|
||||
@@ -35,7 +35,7 @@ export class UserVerificationComponent implements ControlValueAccessor, OnInit {
|
||||
disableRequestOTP = false;
|
||||
sentCode = false;
|
||||
|
||||
secret = new UntypedFormControl("");
|
||||
secret = new FormControl("");
|
||||
|
||||
private onChange: (value: Verification) => void;
|
||||
|
||||
|
||||
@@ -49,7 +49,8 @@ import { SyncService as SyncServiceAbstraction } from "@bitwarden/common/abstrac
|
||||
import { TokenService as TokenServiceAbstraction } from "@bitwarden/common/abstractions/token.service";
|
||||
import { TotpService as TotpServiceAbstraction } from "@bitwarden/common/abstractions/totp.service";
|
||||
import { TwoFactorService as TwoFactorServiceAbstraction } from "@bitwarden/common/abstractions/twoFactor.service";
|
||||
import { UserVerificationService as UserVerificationServiceAbstraction } from "@bitwarden/common/abstractions/userVerification.service";
|
||||
import { UserVerificationApiServiceAbstraction } from "@bitwarden/common/abstractions/userVerification/userVerification-api.service.abstraction";
|
||||
import { UserVerificationService as UserVerificationServiceAbstraction } from "@bitwarden/common/abstractions/userVerification/userVerification.service.abstraction";
|
||||
import { UsernameGenerationService as UsernameGenerationServiceAbstraction } from "@bitwarden/common/abstractions/usernameGeneration.service";
|
||||
import { VaultTimeoutService as VaultTimeoutServiceAbstraction } from "@bitwarden/common/abstractions/vaultTimeout.service";
|
||||
import { StateFactory } from "@bitwarden/common/factories/stateFactory";
|
||||
@@ -89,7 +90,8 @@ import { SyncService } from "@bitwarden/common/services/sync.service";
|
||||
import { TokenService } from "@bitwarden/common/services/token.service";
|
||||
import { TotpService } from "@bitwarden/common/services/totp.service";
|
||||
import { TwoFactorService } from "@bitwarden/common/services/twoFactor.service";
|
||||
import { UserVerificationService } from "@bitwarden/common/services/userVerification.service";
|
||||
import { UserVerificationApiService } from "@bitwarden/common/services/userVerification/userVerification-api.service";
|
||||
import { UserVerificationService } from "@bitwarden/common/services/userVerification/userVerification.service";
|
||||
import { UsernameGenerationService } from "@bitwarden/common/services/usernameGeneration.service";
|
||||
import { VaultTimeoutService } from "@bitwarden/common/services/vaultTimeout.service";
|
||||
import { WebCryptoFunctionService } from "@bitwarden/common/services/webCryptoFunction.service";
|
||||
@@ -476,7 +478,11 @@ export const LOG_MAC_FAILURES = new InjectionToken<string>("LOG_MAC_FAILURES");
|
||||
{
|
||||
provide: UserVerificationServiceAbstraction,
|
||||
useClass: UserVerificationService,
|
||||
deps: [CryptoServiceAbstraction, I18nServiceAbstraction, ApiServiceAbstraction],
|
||||
deps: [
|
||||
CryptoServiceAbstraction,
|
||||
I18nServiceAbstraction,
|
||||
UserVerificationApiServiceAbstraction,
|
||||
],
|
||||
},
|
||||
{ provide: PasswordRepromptServiceAbstraction, useClass: PasswordRepromptService },
|
||||
{
|
||||
@@ -502,6 +508,11 @@ export const LOG_MAC_FAILURES = new InjectionToken<string>("LOG_MAC_FAILURES");
|
||||
provide: FormValidationErrorsServiceAbstraction,
|
||||
useClass: FormValidationErrorsService,
|
||||
},
|
||||
{
|
||||
provide: UserVerificationApiServiceAbstraction,
|
||||
useClass: UserVerificationApiService,
|
||||
deps: [ApiServiceAbstraction],
|
||||
},
|
||||
],
|
||||
})
|
||||
export class JslibServicesModule {}
|
||||
|
||||
@@ -78,25 +78,4 @@ describe("ConsoleLogService", () => {
|
||||
error: { 0: "this is an error message" },
|
||||
});
|
||||
});
|
||||
|
||||
it("times with output to info", async () => {
|
||||
logService.time();
|
||||
await new Promise((r) => setTimeout(r, 250));
|
||||
const duration = logService.timeEnd();
|
||||
expect(duration[0]).toBe(0);
|
||||
expect(duration[1]).toBeGreaterThan(0);
|
||||
expect(duration[1]).toBeLessThan(500 * 10e6);
|
||||
|
||||
expect(caughtMessage).toEqual(expect.arrayContaining([]));
|
||||
expect(caughtMessage.log.length).toBe(1);
|
||||
expect(caughtMessage.log[0]).toEqual(expect.stringMatching(/^default: \d+\.?\d*ms$/));
|
||||
});
|
||||
|
||||
it("filters time output", async () => {
|
||||
logService = new ConsoleLogService(true, () => true);
|
||||
logService.time();
|
||||
logService.timeEnd();
|
||||
|
||||
expect(caughtMessage).toEqual({});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -32,7 +32,7 @@ describe("Folder Service", () => {
|
||||
stateService.getEncryptedFolders().resolves({
|
||||
"1": folderData("1", "test"),
|
||||
});
|
||||
stateService.activeAccount.returns(activeAccount);
|
||||
stateService.activeAccount$.returns(activeAccount);
|
||||
stateService.activeAccountUnlocked.returns(activeAccountUnlocked);
|
||||
(window as any).bitwardenContainerService = new ContainerService(cryptoService);
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { OrganizationApiKeyType } from "../enums/organizationApiKeyType";
|
||||
import { OrganizationConnectionType } from "../enums/organizationConnectionType";
|
||||
import { SetKeyConnectorKeyRequest } from "../models/request/account/setKeyConnectorKeyRequest";
|
||||
import { VerifyOTPRequest } from "../models/request/account/verifyOTPRequest";
|
||||
import { AttachmentRequest } from "../models/request/attachmentRequest";
|
||||
import { BitPayInvoiceRequest } from "../models/request/bitPayInvoiceRequest";
|
||||
import { CipherBulkDeleteRequest } from "../models/request/cipherBulkDeleteRequest";
|
||||
@@ -228,8 +227,6 @@ export abstract class ApiService {
|
||||
postUserApiKey: (id: string, request: SecretVerificationRequest) => Promise<ApiKeyResponse>;
|
||||
postUserRotateApiKey: (id: string, request: SecretVerificationRequest) => Promise<ApiKeyResponse>;
|
||||
putUpdateTempPassword: (request: UpdateTempPasswordRequest) => Promise<any>;
|
||||
postAccountRequestOTP: () => Promise<void>;
|
||||
postAccountVerifyOTP: (request: VerifyOTPRequest) => Promise<void>;
|
||||
postConvertToKeyConnector: () => Promise<void>;
|
||||
|
||||
getUserBillingHistory: () => Promise<BillingHistoryResponse>;
|
||||
|
||||
@@ -6,6 +6,4 @@ export abstract class LogService {
|
||||
warning: (message: string) => void;
|
||||
error: (message: string) => void;
|
||||
write: (level: LogLevelType, message: string) => void;
|
||||
time: (label: string) => void;
|
||||
timeEnd: (label: string) => [number, number];
|
||||
}
|
||||
|
||||
@@ -26,9 +26,8 @@ import { SendView } from "../models/view/sendView";
|
||||
|
||||
export abstract class StateService<T extends Account = Account> {
|
||||
accounts: BehaviorSubject<{ [userId: string]: T }>;
|
||||
activeAccount: BehaviorSubject<string>;
|
||||
|
||||
activeAccountUnlocked: Observable<boolean>;
|
||||
activeAccount$: Observable<string>;
|
||||
activeAccountUnlocked$: Observable<boolean>;
|
||||
|
||||
addAccount: (account: T) => Promise<void>;
|
||||
setActiveUser: (userId: string) => Promise<void>;
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
import { VerifyOTPRequest } from "@bitwarden/common/models/request/account/verifyOTPRequest";
|
||||
|
||||
export abstract class UserVerificationApiServiceAbstraction {
|
||||
postAccountVerifyOTP: (request: VerifyOTPRequest) => Promise<void>;
|
||||
postAccountRequestOTP: () => Promise<void>;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { SecretVerificationRequest } from "../models/request/secretVerificationRequest";
|
||||
import { Verification } from "../types/verification";
|
||||
import { SecretVerificationRequest } from "../../models/request/secretVerificationRequest";
|
||||
import { Verification } from "../../types/verification";
|
||||
|
||||
export abstract class UserVerificationService {
|
||||
buildRequest: <T extends SecretVerificationRequest>(
|
||||
@@ -1,17 +1,28 @@
|
||||
/* eslint-disable no-useless-escape */
|
||||
import * as tldjs from "tldjs";
|
||||
|
||||
import { CryptoService } from "@bitwarden/common/abstractions/crypto.service";
|
||||
|
||||
import { I18nService } from "../abstractions/i18n.service";
|
||||
|
||||
const nodeURL = typeof window === "undefined" ? require("url") : null;
|
||||
|
||||
declare global {
|
||||
/* eslint-disable-next-line no-var */
|
||||
var bitwardenContainerService: BitwardenContainerService;
|
||||
}
|
||||
|
||||
interface BitwardenContainerService {
|
||||
getCryptoService: () => CryptoService;
|
||||
}
|
||||
|
||||
export class Utils {
|
||||
static inited = false;
|
||||
static isNode = false;
|
||||
static isBrowser = true;
|
||||
static isMobileBrowser = false;
|
||||
static isAppleMobileBrowser = false;
|
||||
static global: any = null;
|
||||
static global: typeof global = null;
|
||||
static tldEndingRegex =
|
||||
/.*\.(com|net|org|edu|uk|gov|ca|de|jp|fr|au|ru|ch|io|es|us|co|xyz|info|ly|mil)$/;
|
||||
// Transpiled version of /\p{Emoji_Presentation}/gu using https://mothereff.in/regexpu. Used for compatability in older browsers.
|
||||
@@ -29,16 +40,25 @@ export class Utils {
|
||||
(process as any).release != null &&
|
||||
(process as any).release.name === "node";
|
||||
Utils.isBrowser = typeof window !== "undefined";
|
||||
|
||||
Utils.isMobileBrowser = Utils.isBrowser && this.isMobile(window);
|
||||
Utils.isAppleMobileBrowser = Utils.isBrowser && this.isAppleMobile(window);
|
||||
Utils.global = Utils.isNode && !Utils.isBrowser ? global : window;
|
||||
|
||||
if (Utils.isNode) {
|
||||
Utils.global = global;
|
||||
} else if (Utils.isBrowser) {
|
||||
Utils.global = window;
|
||||
} else {
|
||||
// If it's not browser or node then it must be a service worker
|
||||
Utils.global = self;
|
||||
}
|
||||
}
|
||||
|
||||
static fromB64ToArray(str: string): Uint8Array {
|
||||
if (Utils.isNode) {
|
||||
return new Uint8Array(Buffer.from(str, "base64"));
|
||||
} else {
|
||||
const binaryString = window.atob(str);
|
||||
const binaryString = Utils.global.atob(str);
|
||||
const bytes = new Uint8Array(binaryString.length);
|
||||
for (let i = 0; i < binaryString.length; i++) {
|
||||
bytes[i] = binaryString.charCodeAt(i);
|
||||
@@ -93,7 +113,7 @@ export class Utils {
|
||||
for (let i = 0; i < bytes.byteLength; i++) {
|
||||
binary += String.fromCharCode(bytes[i]);
|
||||
}
|
||||
return window.btoa(binary);
|
||||
return Utils.global.btoa(binary);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -157,7 +177,7 @@ export class Utils {
|
||||
if (Utils.isNode) {
|
||||
return Buffer.from(utfStr, "utf8").toString("base64");
|
||||
} else {
|
||||
return decodeURIComponent(escape(window.btoa(utfStr)));
|
||||
return decodeURIComponent(escape(Utils.global.btoa(utfStr)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,7 +189,7 @@ export class Utils {
|
||||
if (Utils.isNode) {
|
||||
return Buffer.from(b64Str, "base64").toString("utf8");
|
||||
} else {
|
||||
return decodeURIComponent(escape(window.atob(b64Str)));
|
||||
return decodeURIComponent(escape(Utils.global.atob(b64Str)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -384,7 +404,7 @@ export class Utils {
|
||||
return new nodeURL.URL(uriString);
|
||||
} else if (typeof URL === "function") {
|
||||
return new URL(uriString);
|
||||
} else if (window != null) {
|
||||
} else if (typeof window !== "undefined") {
|
||||
const hasProtocol = uriString.indexOf("://") > -1;
|
||||
if (!hasProtocol && uriString.indexOf(".") > -1) {
|
||||
uriString = "http://" + uriString;
|
||||
|
||||
@@ -48,7 +48,7 @@ export class Attachment extends Domain {
|
||||
|
||||
if (this.key != null) {
|
||||
let cryptoService: CryptoService;
|
||||
const containerService = (Utils.global as any).bitwardenContainerService;
|
||||
const containerService = Utils.global.bitwardenContainerService;
|
||||
if (containerService) {
|
||||
cryptoService = containerService.getCryptoService();
|
||||
} else {
|
||||
|
||||
@@ -104,7 +104,7 @@ export class EncString implements IEncrypted {
|
||||
}
|
||||
|
||||
let cryptoService: CryptoService;
|
||||
const containerService = (Utils.global as any).bitwardenContainerService;
|
||||
const containerService = Utils.global.bitwardenContainerService;
|
||||
if (containerService) {
|
||||
cryptoService = containerService.getCryptoService();
|
||||
} else {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { AccountApiService } from "@bitwarden/common/abstractions/account/account-api.service.abstraction";
|
||||
import { LogService } from "@bitwarden/common/abstractions/log.service";
|
||||
import { MessagingService } from "@bitwarden/common/abstractions/messaging.service";
|
||||
import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification.service";
|
||||
import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification/userVerification.service.abstraction";
|
||||
|
||||
import { AccountService as AccountServiceAbstraction } from "../../abstractions/account/account.service.abstraction";
|
||||
import { Verification } from "../../types/verification";
|
||||
@@ -14,7 +14,7 @@ export class AccountService implements AccountServiceAbstraction {
|
||||
private logService: LogService
|
||||
) {}
|
||||
|
||||
async delete(verification: Verification): Promise<any> {
|
||||
async delete(verification: Verification): Promise<void> {
|
||||
try {
|
||||
const verificationRequest = await this.userVerificationService.buildRequest(verification);
|
||||
await this.accountApiService.deleteAccount(verificationRequest);
|
||||
|
||||
@@ -8,7 +8,6 @@ import { OrganizationApiKeyType } from "../enums/organizationApiKeyType";
|
||||
import { OrganizationConnectionType } from "../enums/organizationConnectionType";
|
||||
import { Utils } from "../misc/utils";
|
||||
import { SetKeyConnectorKeyRequest } from "../models/request/account/setKeyConnectorKeyRequest";
|
||||
import { VerifyOTPRequest } from "../models/request/account/verifyOTPRequest";
|
||||
import { AttachmentRequest } from "../models/request/attachmentRequest";
|
||||
import { BitPayInvoiceRequest } from "../models/request/bitPayInvoiceRequest";
|
||||
import { CipherBulkDeleteRequest } from "../models/request/cipherBulkDeleteRequest";
|
||||
@@ -457,14 +456,6 @@ export class ApiService implements ApiServiceAbstraction {
|
||||
return this.send("PUT", "/accounts/update-temp-password", request, true, false);
|
||||
}
|
||||
|
||||
postAccountRequestOTP(): Promise<void> {
|
||||
return this.send("POST", "/accounts/request-otp", null, true, false);
|
||||
}
|
||||
|
||||
postAccountVerifyOTP(request: VerifyOTPRequest): Promise<void> {
|
||||
return this.send("POST", "/accounts/verify-otp", request, true, false);
|
||||
}
|
||||
|
||||
postConvertToKeyConnector(): Promise<void> {
|
||||
return this.send("POST", "/accounts/convert-to-key-connector", null, true, false);
|
||||
}
|
||||
|
||||
@@ -341,7 +341,7 @@ export class CipherService implements CipherServiceAbstraction {
|
||||
throw new Error("No key.");
|
||||
}
|
||||
|
||||
const promises: any[] = [];
|
||||
const promises: Promise<number>[] = [];
|
||||
const ciphers = await this.getAll();
|
||||
ciphers.forEach(async (cipher) => {
|
||||
promises.push(cipher.decrypt().then((c) => decCiphers.push(c)));
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import * as hrtime from "browser-hrtime";
|
||||
|
||||
import { LogService as LogServiceAbstraction } from "../abstractions/log.service";
|
||||
import { LogLevelType } from "../enums/logLevelType";
|
||||
|
||||
@@ -56,17 +54,4 @@ export class ConsoleLogService implements LogServiceAbstraction {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
time(label = "default") {
|
||||
if (!this.timersMap.has(label)) {
|
||||
this.timersMap.set(label, hrtime());
|
||||
}
|
||||
}
|
||||
|
||||
timeEnd(label = "default"): [number, number] {
|
||||
const elapsed = hrtime(this.timersMap.get(label));
|
||||
this.timersMap.delete(label);
|
||||
this.write(LogLevelType.Info, `${label}: ${elapsed[0] * 1000 + elapsed[1] / 10e6}ms`);
|
||||
return elapsed;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ export class EnvironmentService implements EnvironmentServiceAbstraction {
|
||||
private scimUrl: string = null;
|
||||
|
||||
constructor(private stateService: StateService) {
|
||||
this.stateService.activeAccount.subscribe(async () => {
|
||||
this.stateService.activeAccount$.subscribe(async () => {
|
||||
await this.setUrlsFromStorage();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ export class FolderService implements InternalFolderServiceAbstraction {
|
||||
private cipherService: CipherService,
|
||||
private stateService: StateService
|
||||
) {
|
||||
this.stateService.activeAccountUnlocked.subscribe(async (unlocked) => {
|
||||
this.stateService.activeAccountUnlocked$.subscribe(async (unlocked) => {
|
||||
if ((Utils.global as any).bitwardenContainerService == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
24
libs/common/src/services/noopEvent.service.ts
Normal file
24
libs/common/src/services/noopEvent.service.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { EventService } from "@bitwarden/common/abstractions/event.service";
|
||||
import { EventType } from "@bitwarden/common/enums/eventType";
|
||||
|
||||
/**
|
||||
* If you want to use this, don't.
|
||||
* If you think you should use that after the warning, don't.
|
||||
*/
|
||||
export default class NoOpEventService implements EventService {
|
||||
constructor() {
|
||||
if (chrome.runtime.getManifest().manifest_version !== 3) {
|
||||
throw new Error("You are not allowed to use this when not in manifest_version 3");
|
||||
}
|
||||
}
|
||||
|
||||
collect(eventType: EventType, cipherId?: string, uploadImmediately?: boolean) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
uploadEvents(userId?: string) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
clearEvents(userId?: string) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,8 @@ import { CipherView } from "../models/view/cipherView";
|
||||
import { SendView } from "../models/view/sendView";
|
||||
|
||||
export class SearchService implements SearchServiceAbstraction {
|
||||
private static registeredPipeline = false;
|
||||
|
||||
indexedEntityId?: string = null;
|
||||
private indexing = false;
|
||||
private index: lunr.Index = null;
|
||||
@@ -31,8 +33,13 @@ export class SearchService implements SearchServiceAbstraction {
|
||||
}
|
||||
});
|
||||
|
||||
//register lunr pipeline function
|
||||
lunr.Pipeline.registerFunction(this.normalizeAccentsPipelineFunction, "normalizeAccents");
|
||||
// Currently have to ensure this is only done a single time. Lunr allows you to register a function
|
||||
// multiple times but they will add a warning message to the console. The way they do that breaks when ran on a service worker.
|
||||
if (!SearchService.registeredPipeline) {
|
||||
SearchService.registeredPipeline = true;
|
||||
//register lunr pipeline function
|
||||
lunr.Pipeline.registerFunction(this.normalizeAccentsPipelineFunction, "normalizeAccents");
|
||||
}
|
||||
}
|
||||
|
||||
clearIndex(): void {
|
||||
@@ -54,7 +61,6 @@ export class SearchService implements SearchServiceAbstraction {
|
||||
return;
|
||||
}
|
||||
|
||||
this.logService.time("search indexing");
|
||||
this.indexing = true;
|
||||
this.indexedEntityId = indexedEntityId;
|
||||
this.index = null;
|
||||
@@ -95,7 +101,7 @@ export class SearchService implements SearchServiceAbstraction {
|
||||
|
||||
this.indexing = false;
|
||||
|
||||
this.logService.timeEnd("search indexing");
|
||||
this.logService.info("Finished search indexing");
|
||||
}
|
||||
|
||||
async searchCiphers(
|
||||
|
||||
@@ -55,8 +55,11 @@ export class StateService<
|
||||
> implements StateServiceAbstraction<TAccount>
|
||||
{
|
||||
accounts = new BehaviorSubject<{ [userId: string]: TAccount }>({});
|
||||
activeAccount = new BehaviorSubject<string>(null);
|
||||
activeAccountUnlocked = new BehaviorSubject<boolean>(false);
|
||||
private activeAccountSubject = new BehaviorSubject<string>(null);
|
||||
activeAccount$ = this.activeAccountSubject.asObservable();
|
||||
|
||||
private activeAccountUnlockedSubject = new BehaviorSubject<boolean>(false);
|
||||
activeAccountUnlocked$ = this.activeAccountUnlockedSubject.asObservable();
|
||||
|
||||
private hasBeenInited = false;
|
||||
private isRecoveredSession = false;
|
||||
@@ -73,17 +76,17 @@ export class StateService<
|
||||
protected useAccountCache: boolean = true
|
||||
) {
|
||||
// If the account gets changed, verify the new account is unlocked
|
||||
this.activeAccount.subscribe(async (userId) => {
|
||||
if (userId == null && this.activeAccountUnlocked.getValue() == false) {
|
||||
this.activeAccountSubject.subscribe(async (userId) => {
|
||||
if (userId == null && this.activeAccountUnlockedSubject.getValue() == false) {
|
||||
return;
|
||||
} else if (userId == null) {
|
||||
this.activeAccountUnlocked.next(false);
|
||||
this.activeAccountUnlockedSubject.next(false);
|
||||
}
|
||||
|
||||
// FIXME: This should be refactored into AuthService or a similar service,
|
||||
// as checking for the existance of the crypto key is a low level
|
||||
// implementation detail.
|
||||
this.activeAccountUnlocked.next((await this.getCryptoMasterKey()) != null);
|
||||
this.activeAccountUnlockedSubject.next((await this.getCryptoMasterKey()) != null);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -125,7 +128,7 @@ export class StateService<
|
||||
state.activeUserId = storedActiveUser;
|
||||
}
|
||||
await this.pushAccounts();
|
||||
this.activeAccount.next(state.activeUserId);
|
||||
this.activeAccountSubject.next(state.activeUserId);
|
||||
|
||||
return state;
|
||||
});
|
||||
@@ -154,7 +157,7 @@ export class StateService<
|
||||
await this.scaffoldNewAccountStorage(account);
|
||||
await this.setLastActive(new Date().getTime(), { userId: account.profile.userId });
|
||||
await this.setActiveUser(account.profile.userId);
|
||||
this.activeAccount.next(account.profile.userId);
|
||||
this.activeAccountSubject.next(account.profile.userId);
|
||||
}
|
||||
|
||||
async setActiveUser(userId: string): Promise<void> {
|
||||
@@ -162,7 +165,7 @@ export class StateService<
|
||||
await this.updateState(async (state) => {
|
||||
state.activeUserId = userId;
|
||||
await this.storageService.save(keys.activeUserId, userId);
|
||||
this.activeAccount.next(state.activeUserId);
|
||||
this.activeAccountSubject.next(state.activeUserId);
|
||||
return state;
|
||||
});
|
||||
|
||||
@@ -497,12 +500,12 @@ export class StateService<
|
||||
this.reconcileOptions(options, await this.defaultInMemoryOptions())
|
||||
);
|
||||
|
||||
if (options.userId == this.activeAccount.getValue()) {
|
||||
if (options.userId == this.activeAccountSubject.getValue()) {
|
||||
const nextValue = value != null;
|
||||
|
||||
// Avoid emitting if we are already unlocked
|
||||
if (this.activeAccountUnlocked.getValue() != nextValue) {
|
||||
this.activeAccountUnlocked.next(nextValue);
|
||||
if (this.activeAccountUnlockedSubject.getValue() != nextValue) {
|
||||
this.activeAccountUnlockedSubject.next(nextValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -607,7 +610,7 @@ export class StateService<
|
||||
);
|
||||
}
|
||||
|
||||
@withPrototypeForArrayMembers(CipherView)
|
||||
@withPrototypeForArrayMembers(CipherView, CipherView.fromJSON)
|
||||
async getDecryptedCiphers(options?: StorageOptions): Promise<CipherView[]> {
|
||||
return (
|
||||
await this.getAccount(this.reconcileOptions(options, await this.defaultInMemoryOptions()))
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import { ApiService } from "../../abstractions/api.service";
|
||||
import { UserVerificationApiServiceAbstraction } from "../../abstractions/userVerification/userVerification-api.service.abstraction";
|
||||
import { VerifyOTPRequest } from "../../models/request/account/verifyOTPRequest";
|
||||
|
||||
export class UserVerificationApiService implements UserVerificationApiServiceAbstraction {
|
||||
constructor(private apiService: ApiService) {}
|
||||
|
||||
postAccountVerifyOTP(request: VerifyOTPRequest): Promise<void> {
|
||||
return this.apiService.send("POST", "/accounts/verify-otp", request, true, false);
|
||||
}
|
||||
async postAccountRequestOTP(): Promise<void> {
|
||||
return this.apiService.send("POST", "/accounts/request-otp", null, true, false);
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
import { ApiService } from "../abstractions/api.service";
|
||||
import { CryptoService } from "../abstractions/crypto.service";
|
||||
import { I18nService } from "../abstractions/i18n.service";
|
||||
import { UserVerificationService as UserVerificationServiceAbstraction } from "../abstractions/userVerification.service";
|
||||
import { VerificationType } from "../enums/verificationType";
|
||||
import { VerifyOTPRequest } from "../models/request/account/verifyOTPRequest";
|
||||
import { SecretVerificationRequest } from "../models/request/secretVerificationRequest";
|
||||
import { Verification } from "../types/verification";
|
||||
import { CryptoService } from "../../abstractions/crypto.service";
|
||||
import { I18nService } from "../../abstractions/i18n.service";
|
||||
import { UserVerificationApiServiceAbstraction } from "../../abstractions/userVerification/userVerification-api.service.abstraction";
|
||||
import { UserVerificationService as UserVerificationServiceAbstraction } from "../../abstractions/userVerification/userVerification.service.abstraction";
|
||||
import { VerificationType } from "../../enums/verificationType";
|
||||
import { VerifyOTPRequest } from "../../models/request/account/verifyOTPRequest";
|
||||
import { SecretVerificationRequest } from "../../models/request/secretVerificationRequest";
|
||||
import { Verification } from "../../types/verification";
|
||||
|
||||
/**
|
||||
* Used for general-purpose user verification throughout the app.
|
||||
@@ -15,7 +15,7 @@ export class UserVerificationService implements UserVerificationServiceAbstracti
|
||||
constructor(
|
||||
private cryptoService: CryptoService,
|
||||
private i18nService: I18nService,
|
||||
private apiService: ApiService
|
||||
private userVerificationApiService: UserVerificationApiServiceAbstraction
|
||||
) {}
|
||||
|
||||
/**
|
||||
@@ -56,7 +56,7 @@ export class UserVerificationService implements UserVerificationServiceAbstracti
|
||||
if (verification.type === VerificationType.OTP) {
|
||||
const request = new VerifyOTPRequest(verification.secret);
|
||||
try {
|
||||
await this.apiService.postAccountVerifyOTP(request);
|
||||
await this.userVerificationApiService.postAccountVerifyOTP(request);
|
||||
} catch (e) {
|
||||
throw new Error(this.i18nService.t("invalidVerificationCode"));
|
||||
}
|
||||
@@ -73,7 +73,7 @@ export class UserVerificationService implements UserVerificationServiceAbstracti
|
||||
}
|
||||
|
||||
async requestOTP() {
|
||||
await this.apiService.postAccountRequestOTP();
|
||||
await this.userVerificationApiService.postAccountRequestOTP();
|
||||
}
|
||||
|
||||
private validateInput(verification: Verification) {
|
||||
@@ -9,7 +9,7 @@ export class WebCryptoFunctionService implements CryptoFunctionService {
|
||||
private crypto: Crypto;
|
||||
private subtle: SubtleCrypto;
|
||||
|
||||
constructor(win: Window) {
|
||||
constructor(win: Window | typeof global) {
|
||||
this.crypto = typeof win.crypto !== "undefined" ? win.crypto : null;
|
||||
this.subtle =
|
||||
!!this.crypto && typeof win.crypto.subtle !== "undefined" ? win.crypto.subtle : null;
|
||||
|
||||
12
libs/common/src/types/authResponse.ts
Normal file
12
libs/common/src/types/authResponse.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { VerificationType } from "../enums/verificationType";
|
||||
|
||||
import { TwoFactorResponse } from "./twoFactorResponse";
|
||||
|
||||
export type AuthResponseBase = {
|
||||
secret: string;
|
||||
verificationType: VerificationType;
|
||||
};
|
||||
|
||||
export type AuthResponse<T extends TwoFactorResponse> = AuthResponseBase & {
|
||||
response: T;
|
||||
};
|
||||
14
libs/common/src/types/twoFactorResponse.ts
Normal file
14
libs/common/src/types/twoFactorResponse.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { TwoFactorAuthenticatorResponse } from "../models/response/twoFactorAuthenticatorResponse";
|
||||
import { TwoFactorDuoResponse } from "../models/response/twoFactorDuoResponse";
|
||||
import { TwoFactorEmailResponse } from "../models/response/twoFactorEmailResponse";
|
||||
import { TwoFactorRecoverResponse } from "../models/response/twoFactorRescoverResponse";
|
||||
import { TwoFactorWebAuthnResponse } from "../models/response/twoFactorWebAuthnResponse";
|
||||
import { TwoFactorYubiKeyResponse } from "../models/response/twoFactorYubiKeyResponse";
|
||||
|
||||
export type TwoFactorResponse =
|
||||
| TwoFactorRecoverResponse
|
||||
| TwoFactorDuoResponse
|
||||
| TwoFactorEmailResponse
|
||||
| TwoFactorWebAuthnResponse
|
||||
| TwoFactorAuthenticatorResponse
|
||||
| TwoFactorYubiKeyResponse;
|
||||
@@ -5,23 +5,48 @@ import { ipcMain } from "electron";
|
||||
import { AbstractStorageService } from "@bitwarden/common/abstractions/storage.service";
|
||||
import { NodeUtils } from "@bitwarden/common/misc/nodeUtils";
|
||||
|
||||
// See: https://github.com/sindresorhus/electron-store/blob/main/index.d.ts
|
||||
interface ElectronStoreOptions {
|
||||
defaults: unknown;
|
||||
name: string;
|
||||
}
|
||||
|
||||
type ElectronStoreConstructor = new (options: ElectronStoreOptions) => ElectronStore;
|
||||
|
||||
// eslint-disable-next-line
|
||||
const Store = require("electron-store");
|
||||
const Store: ElectronStoreConstructor = require("electron-store");
|
||||
|
||||
interface ElectronStore {
|
||||
get: (key: string) => unknown;
|
||||
set: (key: string, obj: unknown) => void;
|
||||
delete: (key: string) => void;
|
||||
}
|
||||
|
||||
interface BaseOptions<T extends string> {
|
||||
action: T;
|
||||
key: string;
|
||||
}
|
||||
|
||||
interface SaveOptions extends BaseOptions<"save"> {
|
||||
obj: unknown;
|
||||
}
|
||||
|
||||
type Options = BaseOptions<"get"> | BaseOptions<"has"> | SaveOptions | BaseOptions<"remove">;
|
||||
|
||||
export class ElectronStorageService implements AbstractStorageService {
|
||||
private store: any;
|
||||
private store: ElectronStore;
|
||||
|
||||
constructor(dir: string, defaults = {}) {
|
||||
if (!fs.existsSync(dir)) {
|
||||
NodeUtils.mkdirpSync(dir, "700");
|
||||
}
|
||||
const storeConfig: any = {
|
||||
const storeConfig: ElectronStoreOptions = {
|
||||
defaults: defaults,
|
||||
name: "data",
|
||||
};
|
||||
this.store = new Store(storeConfig);
|
||||
|
||||
ipcMain.handle("storageService", (event, options) => {
|
||||
ipcMain.handle("storageService", (event, options: Options) => {
|
||||
switch (options.action) {
|
||||
case "get":
|
||||
return this.get(options.key);
|
||||
@@ -45,7 +70,7 @@ export class ElectronStorageService implements AbstractStorageService {
|
||||
return Promise.resolve(val != null);
|
||||
}
|
||||
|
||||
save(key: string, obj: any): Promise<any> {
|
||||
save(key: string, obj: unknown): Promise<void> {
|
||||
if (obj instanceof Set) {
|
||||
obj = Array.from(obj);
|
||||
}
|
||||
@@ -53,7 +78,7 @@ export class ElectronStorageService implements AbstractStorageService {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
remove(key: string): Promise<any> {
|
||||
remove(key: string): Promise<void> {
|
||||
this.store.delete(key);
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user