diff --git a/jslib b/jslib index f9042408f4..cea09a22e5 160000 --- a/jslib +++ b/jslib @@ -1 +1 @@ -Subproject commit f9042408f44b299a7cdd1286490b70f5fd2db999 +Subproject commit cea09a22e533ef3598bb497ba0503c2fcd5b2dc1 diff --git a/src/_locales/en/messages.json b/src/_locales/en/messages.json index 94e5f5ad63..cdcbd64736 100644 --- a/src/_locales/en/messages.json +++ b/src/_locales/en/messages.json @@ -606,6 +606,9 @@ "message": "WARNING", "description": "WARNING (should stay in capitalized letters if the language permits)" }, + "confirmVaultExport": { + "message": "Confirm Vault Export" + }, "exportWarningDesc": { "message": "This export contains your vault data in an unencrypted format. You should not store or send the exported file over unsecure channels (such as email). Delete it immediately after you are done using it." }, @@ -1431,5 +1434,8 @@ }, "personalOwnershipSubmitError": { "message": "Due to an Enterprise Policy, you are restricted from saving items to your personal vault. Change the Ownership option to an organization and choose from available Collections." + }, + "personalOwnershipPolicyInEffect": { + "message": "An organization policy is affecting your ownership options." } } diff --git a/src/background/nativeMessaging.background.ts b/src/background/nativeMessaging.background.ts index a8db0cbf04..ddd9317da8 100644 --- a/src/background/nativeMessaging.background.ts +++ b/src/background/nativeMessaging.background.ts @@ -115,9 +115,7 @@ export class NativeMessagingBackground { error = chrome.runtime.lastError.message; } - if (error === 'Specified native messaging host not found.' || - error === 'Access to the specified native messaging host is forbidden.' || - error === 'An unexpected error occurred') { + if (error != null) { this.messagingService.send('showDialog', { text: this.i18nService.t('desktopIntegrationDisabledDesc'), title: this.i18nService.t('desktopIntegrationDisabledTitle'), @@ -145,7 +143,7 @@ export class NativeMessagingBackground { message.timestamp = Date.now(); const encrypted = await this.cryptoService.encrypt(JSON.stringify(message), this.sharedSecret); - this.port.postMessage({appId: this.appId, message: encrypted}); + this.postMessage({appId: this.appId, message: encrypted}); } getResponse(): Promise { @@ -154,6 +152,27 @@ export class NativeMessagingBackground { }); } + private postMessage(message: any) { + // Wrap in try-catch to when the port disconnected without triggering `onDisconnect`. + try { + this.port.postMessage(message); + } catch (e) { + // tslint:disable-next-line + console.error("NativeMessaging port disconnected, disconnecting."); + + this.sharedSecret = null; + this.privateKey = null; + this.connected = false; + + this.messagingService.send('showDialog', { + text: this.i18nService.t('nativeMessagingInvalidEncryptionDesc'), + title: this.i18nService.t('nativeMessagingInvalidEncryptionTitle'), + confirmText: this.i18nService.t('ok'), + type: 'error', + }); + } + } + private async onMessage(rawMessage: any) { const message = JSON.parse(await this.cryptoService.decryptToUtf8(rawMessage, this.sharedSecret)); @@ -231,7 +250,7 @@ export class NativeMessagingBackground { message.timestamp = Date.now(); - this.port.postMessage({appId: this.appId, message: message}); + this.postMessage({appId: this.appId, message: message}); } private async showFingerprintDialog() { diff --git a/src/browser/safariApp.ts b/src/browser/safariApp.ts index 5593b069b8..894ae2c6d4 100644 --- a/src/browser/safariApp.ts +++ b/src/browser/safariApp.ts @@ -25,12 +25,20 @@ export class SafariApp { return new Promise((resolve) => { const now = new Date(); const messageId = now.getTime().toString() + '_' + Math.floor(Math.random() * Number.MAX_SAFE_INTEGER); - (window as any).webkit.messageHandlers.bitwardenApp.postMessage(JSON.stringify({ - id: messageId, - command: command, - data: data, - responseData: null, - })); + if (typeof safari === typeof undefined) { + (window as any).webkit.messageHandlers.bitwardenApp.postMessage(JSON.stringify({ + id: messageId, + command: command, + data: data, + responseData: null, + })); + } else { + safari.extension.dispatchMessage('bitwarden', { + command: command, + data: data, + responseData: null, + }); + } if (resolveNow) { resolve(); } else { diff --git a/src/content/sso.ts b/src/content/sso.ts index 508bc2aea3..a127a39d55 100644 --- a/src/content/sso.ts +++ b/src/content/sso.ts @@ -3,6 +3,15 @@ window.addEventListener('message', (event) => { return; if (event.data.command && (event.data.command === 'authResult')) { + if (typeof chrome === typeof undefined) { + safari.extension.dispatchMessage('bitwarden', { + command: event.data.command, + code: event.data.code, + state: event.data.state, + referrer: event.source.location.hostname, + }); + return; + } chrome.runtime.sendMessage({ command: event.data.command, code: event.data.code, diff --git a/src/popup/accounts/sso.component.ts b/src/popup/accounts/sso.component.ts index c88f3d1e60..0867b781a7 100644 --- a/src/popup/accounts/sso.component.ts +++ b/src/popup/accounts/sso.component.ts @@ -41,11 +41,11 @@ export class SsoComponent extends BaseSsoComponent { this.redirectUri = url + '/sso-connector.html'; this.clientId = 'browser'; - super.onSuccessfulLogin = () => { + super.onSuccessfulLogin = async () => { + await syncService.fullSync(true); BrowserApi.reloadOpenWindows(); const thisWindow = window.open('', '_self'); thisWindow.close(); - return syncService.fullSync(true); }; } } diff --git a/src/popup/components/action-buttons.component.ts b/src/popup/components/action-buttons.component.ts index ab22d38678..909d20b8e2 100644 --- a/src/popup/components/action-buttons.component.ts +++ b/src/popup/components/action-buttons.component.ts @@ -57,7 +57,7 @@ export class ActionButtonsComponent { } async copy(cipher: CipherView, value: string, typeI18nKey: string, aType: string) { - if (value == null || !this.displayTotpCopyButton(cipher)) { + if (value == null || aType === 'TOTP' && !this.displayTotpCopyButton(cipher)) { return; } else if (value === cipher.login.totp) { value = await this.totpService.getCode(value); diff --git a/src/popup/settings/export.component.html b/src/popup/settings/export.component.html index f4a90062c9..759fbec53c 100644 --- a/src/popup/settings/export.component.html +++ b/src/popup/settings/export.component.html @@ -41,13 +41,6 @@ diff --git a/src/popup/vault/add-edit.component.html b/src/popup/vault/add-edit.component.html index c2961321b2..a94e8217da 100644 --- a/src/popup/vault/add-edit.component.html +++ b/src/popup/vault/add-edit.component.html @@ -14,6 +14,9 @@ + + {{'personalOwnershipPolicyInEffect' | i18n}} +
{{'itemInformation' | i18n}} diff --git a/src/safari/safari/SafariExtensionViewController.swift b/src/safari/safari/SafariExtensionViewController.swift index 0f2ac13eaa..0241df00ce 100644 --- a/src/safari/safari/SafariExtensionViewController.swift +++ b/src/safari/safari/SafariExtensionViewController.swift @@ -16,14 +16,10 @@ class SafariExtensionViewController: SFSafariExtensionViewController, WKScriptMe if initedWebView { return } - let version = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String initedWebView = true let parentHeight = SafariExtensionViewController.shared.preferredContentSize.height let parentWidth = SafariExtensionViewController.shared.preferredContentSize.width let webViewConfig = WKWebViewConfiguration() - let bundleURL = Bundle.main.resourceURL!.absoluteURL - let html = bundleURL.appendingPathComponent("app/popup/index.html") - let url = URL(string: "\(html.absoluteString)?appVersion=\(version!)") webViewConfig.preferences.setValue(true, forKey: "allowFileAccessFromFileURLs") webViewConfig.preferences.setValue(true, forKey: "developerExtrasEnabled") webViewConfig.userContentController.add(self, name: "bitwardenApp") @@ -31,12 +27,26 @@ class SafariExtensionViewController: SFSafariExtensionViewController, WKScriptMe configuration: webViewConfig) webView.navigationDelegate = self webView.allowsLinkPreview = false - webView.loadFileURL(url!, allowingReadAccessTo: bundleURL) + navigateWebView("app/popup/index.html") webView.alphaValue = 0.0 webView.uiDelegate = self view.addSubview(webView) } + func navigateWebView(_ relativeUrl: String){ + let version = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String + let bundleUrl = Bundle.main.resourceURL!.absoluteURL + + if var urlComponents = URLComponents(string: bundleUrl.absoluteString + relativeUrl) { + if (urlComponents.queryItems?.first(where: { $0.name == "appVersion" })?.value == nil) { + urlComponents.queryItems = urlComponents.queryItems ?? [] + urlComponents.queryItems!.append(URLQueryItem(name: "appVersion", value: version)) + } + + webView.loadFileURL(urlComponents.url!, allowingReadAccessTo: bundleUrl) + } + } + func webView(_ webView: WKWebView, didFinish _: WKNavigation!) { if #available(OSXApplicationExtension 10.12, *) { NSAnimationContext.runAnimationGroup({ _ in @@ -179,6 +189,14 @@ class SafariExtensionViewController: SFSafariExtensionViewController, WKScriptMe replyMessage(message: m) } else if command == "createNewTab" { if let data = m.data, let url = URL(string: data) { + if !data.starts(with: "https://") && !data.starts(with: "http://") { + SFSafariApplication.getActiveWindow { win in + win?.getToolbarItem(completionHandler: { item in + item?.showPopover() + self.navigateWebView("app/" + url.absoluteString) + }) + } + } SFSafariApplication.getActiveWindow { win in win?.openTab(with: url, makeActiveIfPossible: true, completionHandler: { _ in // Tab opened diff --git a/src/services/autofill.service.ts b/src/services/autofill.service.ts index 7498248bbe..82d5224a6e 100644 --- a/src/services/autofill.service.ts +++ b/src/services/autofill.service.ts @@ -254,7 +254,7 @@ export default class AutofillService implements AutofillServiceInterface { } } - const autoFillResponse = await this.doAutoFill({ + const totpCode = await this.doAutoFill({ cipher: cipher, pageDetails: pageDetails, skipTotp: !fromCommand, @@ -265,12 +265,12 @@ export default class AutofillService implements AutofillServiceInterface { fillNewPassword: fromCommand, }); - // Only update last used index if doAutoFill didn't throw an exception + // Update last used index as autofill has succeed if (fromCommand) { this.cipherService.updateLastUsedIndexForUrl(tab.url); } - return autoFillResponse; + return totpCode; } // Helpers