mirror of
https://github.com/bitwarden/browser
synced 2025-12-20 10:13:31 +00:00
Merge branch 'master' into copy-totp-on-auto-fill
This commit is contained in:
@@ -20,6 +20,7 @@ import {
|
||||
} from 'jslib/abstractions';
|
||||
|
||||
import { EventService } from 'jslib/abstractions/event.service';
|
||||
import { CipherRepromptType } from 'jslib/enums/cipherRepromptType';
|
||||
import { EventType } from 'jslib/enums/eventType';
|
||||
|
||||
const CardAttributes: string[] = ['autoCompleteType', 'data-stripe', 'htmlName', 'htmlID', 'label-tag',
|
||||
@@ -254,9 +255,13 @@ export default class AutofillService implements AutofillServiceInterface {
|
||||
}
|
||||
}
|
||||
|
||||
if (cipher.reprompt !== CipherRepromptType.None) {
|
||||
return;
|
||||
}
|
||||
|
||||
const copyTotpOnAutoFill = await this.totpService.isAutoCopyOnAutoFillEnabled();
|
||||
const shouldCopyTotp = fromCommand || copyTotpOnAutoFill;
|
||||
|
||||
|
||||
const totpCode = await this.doAutoFill({
|
||||
cipher: cipher,
|
||||
pageDetails: pageDetails,
|
||||
|
||||
@@ -6,16 +6,14 @@ import { DeviceType } from 'jslib/enums/deviceType';
|
||||
import { MessagingService } from 'jslib/abstractions/messaging.service';
|
||||
import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service';
|
||||
|
||||
import { AnalyticsIds } from 'jslib/misc/analytics';
|
||||
|
||||
const DialogPromiseExpiration = 600000; // 10 minutes
|
||||
|
||||
export default class BrowserPlatformUtilsService implements PlatformUtilsService {
|
||||
identityClientId: string = 'browser';
|
||||
|
||||
private showDialogResolves = new Map<number, { resolve: (value: boolean) => void, date: Date }>();
|
||||
private passwordDialogResolves = new Map<number, { tryResolve: (canceled: boolean, password: string) => Promise<boolean>, date: Date }>();
|
||||
private deviceCache: DeviceType = null;
|
||||
private analyticsIdCache: string = null;
|
||||
private prefersColorSchemeDark = window.matchMedia('(prefers-color-scheme: dark)');
|
||||
|
||||
constructor(private messagingService: MessagingService,
|
||||
@@ -82,15 +80,6 @@ export default class BrowserPlatformUtilsService implements PlatformUtilsService
|
||||
return false;
|
||||
}
|
||||
|
||||
analyticsId(): string {
|
||||
if (this.analyticsIdCache) {
|
||||
return this.analyticsIdCache;
|
||||
}
|
||||
|
||||
this.analyticsIdCache = (AnalyticsIds as any)[this.getDevice()];
|
||||
return this.analyticsIdCache;
|
||||
}
|
||||
|
||||
async isViewOpen(): Promise<boolean> {
|
||||
if (await BrowserApi.isPopupOpen()) {
|
||||
return true;
|
||||
@@ -122,16 +111,12 @@ export default class BrowserPlatformUtilsService implements PlatformUtilsService
|
||||
BrowserApi.downloadFile(win, blobData, blobOptions, fileName);
|
||||
}
|
||||
|
||||
getApplicationVersion(): string {
|
||||
return BrowserApi.getApplicationVersion();
|
||||
getApplicationVersion(): Promise<string> {
|
||||
return Promise.resolve(BrowserApi.getApplicationVersion());
|
||||
}
|
||||
|
||||
supportsU2f(win: Window): boolean {
|
||||
if (win != null && (win as any).u2f != null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return this.isChrome() || this.isOpera() || this.isVivaldi();
|
||||
supportsWebAuthn(win: Window): boolean {
|
||||
return (typeof(PublicKeyCredential) !== 'undefined');
|
||||
}
|
||||
|
||||
supportsDuo(): boolean {
|
||||
@@ -148,10 +133,12 @@ export default class BrowserPlatformUtilsService implements PlatformUtilsService
|
||||
});
|
||||
}
|
||||
|
||||
showDialog(text: string, title?: string, confirmText?: string, cancelText?: string, type?: string) {
|
||||
showDialog(body: string, title?: string, confirmText?: string, cancelText?: string, type?: string,
|
||||
bodyIsHtml: boolean = false) {
|
||||
const dialogId = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER);
|
||||
this.messagingService.send('showDialog', {
|
||||
text: text,
|
||||
text: bodyIsHtml ? null : body,
|
||||
html: bodyIsHtml ? body : null,
|
||||
title: title,
|
||||
confirmText: confirmText,
|
||||
cancelText: cancelText,
|
||||
@@ -163,11 +150,30 @@ export default class BrowserPlatformUtilsService implements PlatformUtilsService
|
||||
});
|
||||
}
|
||||
|
||||
eventTrack(action: string, label?: string, options?: any) {
|
||||
this.messagingService.send('analyticsEventTrack', {
|
||||
action: action,
|
||||
label: label,
|
||||
options: options,
|
||||
async showPasswordDialog(title: string, body: string, passwordValidation: (value: string) => Promise<boolean>) {
|
||||
const dialogId = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER);
|
||||
|
||||
this.messagingService.send('showPasswordDialog', {
|
||||
title: title,
|
||||
body: body,
|
||||
dialogId: dialogId,
|
||||
});
|
||||
|
||||
return new Promise<boolean>(resolve => {
|
||||
this.passwordDialogResolves.set(dialogId, {
|
||||
tryResolve: async (canceled: boolean, password: string) => {
|
||||
if (canceled) {
|
||||
resolve(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (await passwordValidation(password)) {
|
||||
resolve(true);
|
||||
return true;
|
||||
}
|
||||
},
|
||||
date: new Date(),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -278,20 +284,41 @@ export default class BrowserPlatformUtilsService implements PlatformUtilsService
|
||||
}
|
||||
|
||||
// Clean up old promises
|
||||
const deleteIds: number[] = [];
|
||||
this.showDialogResolves.forEach((val, key) => {
|
||||
const age = new Date().getTime() - val.date.getTime();
|
||||
if (age > DialogPromiseExpiration) {
|
||||
deleteIds.push(key);
|
||||
this.showDialogResolves.delete(key);
|
||||
}
|
||||
});
|
||||
deleteIds.forEach(id => {
|
||||
this.showDialogResolves.delete(id);
|
||||
});
|
||||
}
|
||||
|
||||
supportsBiometric() {
|
||||
return Promise.resolve(!this.isFirefox() && !this.isSafari());
|
||||
async resolvePasswordDialogPromise(dialogId: number, canceled: boolean, password: string): Promise<boolean> {
|
||||
let result = false;
|
||||
if (this.passwordDialogResolves.has(dialogId)) {
|
||||
const resolveObj = this.passwordDialogResolves.get(dialogId);
|
||||
if (await resolveObj.tryResolve(canceled, password)) {
|
||||
this.passwordDialogResolves.delete(dialogId);
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up old promises
|
||||
this.passwordDialogResolves.forEach((val, key) => {
|
||||
const age = new Date().getTime() - val.date.getTime();
|
||||
if (age > DialogPromiseExpiration) {
|
||||
this.passwordDialogResolves.delete(key);
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
async supportsBiometric() {
|
||||
if (this.isFirefox()) {
|
||||
return parseInt((await browser.runtime.getBrowserInfo()).version.split('.')[0], 10) >= 87;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
authenticateBiometric() {
|
||||
@@ -312,8 +339,8 @@ export default class BrowserPlatformUtilsService implements PlatformUtilsService
|
||||
return false;
|
||||
}
|
||||
|
||||
getDefaultSystemTheme() {
|
||||
return this.prefersColorSchemeDark.matches ? 'dark' : 'light';
|
||||
getDefaultSystemTheme(): Promise<'light' | 'dark'> {
|
||||
return Promise.resolve(this.prefersColorSchemeDark.matches ? 'dark' : 'light');
|
||||
}
|
||||
|
||||
onDefaultSystemThemeChange(callback: ((theme: 'light' | 'dark') => unknown)) {
|
||||
|
||||
@@ -22,7 +22,7 @@ export default class BrowserStorageService implements StorageService {
|
||||
async save(key: string, obj: any): Promise<any> {
|
||||
if (obj == null) {
|
||||
// Fix safari not liking null in set
|
||||
return new Promise(resolve => {
|
||||
return new Promise<void>(resolve => {
|
||||
this.chromeStorageApi.remove(key, () => {
|
||||
resolve();
|
||||
});
|
||||
@@ -30,7 +30,7 @@ export default class BrowserStorageService implements StorageService {
|
||||
}
|
||||
|
||||
const keyedObj = { [key]: obj };
|
||||
return new Promise(resolve => {
|
||||
return new Promise<void>(resolve => {
|
||||
this.chromeStorageApi.set(keyedObj, () => {
|
||||
resolve();
|
||||
});
|
||||
@@ -38,7 +38,7 @@ export default class BrowserStorageService implements StorageService {
|
||||
}
|
||||
|
||||
async remove(key: string): Promise<any> {
|
||||
return new Promise(resolve => {
|
||||
return new Promise<void>(resolve => {
|
||||
this.chromeStorageApi.remove(key, () => {
|
||||
resolve();
|
||||
});
|
||||
|
||||
29
src/services/vaultTimeout.service.ts
Normal file
29
src/services/vaultTimeout.service.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import { VaultTimeoutService as BaseVaultTimeoutService } from 'jslib/services/vaultTimeout.service';
|
||||
import { SafariApp } from '../browser/safariApp';
|
||||
|
||||
export default class VaultTimeoutService extends BaseVaultTimeoutService {
|
||||
|
||||
startCheck() {
|
||||
this.checkVaultTimeout();
|
||||
if (this.platformUtilsService.isSafari()) {
|
||||
this.checkSafari();
|
||||
} else {
|
||||
setInterval(() => this.checkVaultTimeout(), 10 * 1000); // check every 10 seconds
|
||||
}
|
||||
}
|
||||
|
||||
// This is a work-around to safari adding an arbitary delay to setTimeout and
|
||||
// setIntervals. It works by calling the native extension which sleeps for 10s,
|
||||
// efficiently replicating setInterval.
|
||||
async checkSafari() {
|
||||
while (true) {
|
||||
try {
|
||||
await SafariApp.sendMessageToApp('sleep');
|
||||
this.checkVaultTimeout();
|
||||
} catch (e) {
|
||||
// tslint:disable-next-line
|
||||
console.log('Exception Safari VaultTimeout', e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user