diff --git a/apps/browser/src/autofill/background/notification.background.ts b/apps/browser/src/autofill/background/notification.background.ts index ca0149bd729..85e8633b2b9 100644 --- a/apps/browser/src/autofill/background/notification.background.ts +++ b/apps/browser/src/autofill/background/notification.background.ts @@ -10,8 +10,6 @@ import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.servi import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; import { CipherType } from "@bitwarden/common/vault/enums/cipher-type"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; -import { LoginUriView } from "@bitwarden/common/vault/models/view/login-uri.view"; -import { LoginView } from "@bitwarden/common/vault/models/view/login.view"; import AddChangePasswordQueueMessage from "../../background/models/addChangePasswordQueueMessage"; import AddLoginQueueMessage from "../../background/models/addLoginQueueMessage"; @@ -95,7 +93,7 @@ export default class NotificationBackground { await BrowserApi.tabSendMessageData(sender.tab, "promptForLogin"); return; } - await this.saveOrUpdateCredentials(sender.tab, msg.folder); + await this.saveOrUpdateCredentials(sender.tab, msg.edit, msg.folder); break; case "bgNeverSave": await this.saveNever(sender.tab); @@ -168,6 +166,7 @@ export default class NotificationBackground { typeData: { isVaultLocked: this.notificationQueue[i].wasVaultLocked, theme: await this.getCurrentTheme(), + removeIndividualVault: await this.removeIndividualVault(), }, }); } else if (this.notificationQueue[i].type === NotificationQueueMessageType.ChangePassword) { @@ -225,10 +224,6 @@ export default class NotificationBackground { return; } - if (!(await this.allowPersonalOwnership())) { - return; - } - this.pushAddLoginToQueue(loginDomain, loginInfo, tab, true); return; } @@ -242,10 +237,6 @@ export default class NotificationBackground { return; } - if (!(await this.allowPersonalOwnership())) { - return; - } - this.pushAddLoginToQueue(loginDomain, loginInfo, tab); } else if ( usernameMatches.length === 1 && @@ -332,14 +323,10 @@ export default class NotificationBackground { await this.checkNotificationQueue(tab); } - private async saveOrUpdateCredentials(tab: chrome.tabs.Tab, folderId?: string) { + private async saveOrUpdateCredentials(tab: chrome.tabs.Tab, edit: boolean, folderId?: string) { for (let i = this.notificationQueue.length - 1; i >= 0; i--) { const queueMessage = this.notificationQueue[i]; - if ( - queueMessage.tabId !== tab.id || - (queueMessage.type !== NotificationQueueMessageType.AddLogin && - queueMessage.type !== NotificationQueueMessageType.ChangePassword) - ) { + if (queueMessage.tabId !== tab.id || !(queueMessage.type in NotificationQueueMessageType)) { continue; } @@ -352,63 +339,79 @@ export default class NotificationBackground { BrowserApi.tabSendMessageData(tab, "closeNotificationBar"); if (queueMessage.type === NotificationQueueMessageType.ChangePassword) { - const changePasswordMessage = queueMessage as AddChangePasswordQueueMessage; - const cipher = await this.getDecryptedCipherById(changePasswordMessage.cipherId); - if (cipher == null) { - return; - } - await this.updateCipher(cipher, changePasswordMessage.newPassword); + const cipherView = await this.getDecryptedCipherById(queueMessage.cipherId); + await this.updatePassword(cipherView, queueMessage.newPassword, edit, tab); return; } if (queueMessage.type === NotificationQueueMessageType.AddLogin) { - if (!queueMessage.wasVaultLocked) { - await this.createNewCipher(queueMessage as AddLoginQueueMessage, folderId); - BrowserApi.tabSendMessageData(tab, "addedCipher"); - return; - } - // If the vault was locked, check if a cipher needs updating instead of creating a new one - const addLoginMessage = queueMessage as AddLoginQueueMessage; - const ciphers = await this.cipherService.getAllDecryptedForUrl(addLoginMessage.uri); - const usernameMatches = ciphers.filter( - (c) => - c.login.username != null && c.login.username.toLowerCase() === addLoginMessage.username - ); + if (queueMessage.wasVaultLocked) { + const allCiphers = await this.cipherService.getAllDecryptedForUrl(queueMessage.uri); + const existingCipher = allCiphers.find( + (c) => + c.login.username != null && c.login.username.toLowerCase() === queueMessage.username + ); - if (usernameMatches.length >= 1) { - await this.updateCipher(usernameMatches[0], addLoginMessage.password); + if (existingCipher != null) { + await this.updatePassword(existingCipher, queueMessage.password, edit, tab); + return; + } + } + + folderId = (await this.folderExists(folderId)) ? folderId : null; + const newCipher = AddLoginQueueMessage.toCipherView(queueMessage, folderId); + + if (edit) { + await this.editItem(newCipher, tab); return; } - await this.createNewCipher(addLoginMessage, folderId); + const cipher = await this.cipherService.encrypt(newCipher); + await this.cipherService.createWithServer(cipher); BrowserApi.tabSendMessageData(tab, "addedCipher"); } } } - private async createNewCipher(queueMessage: AddLoginQueueMessage, folderId: string) { - const loginModel = new LoginView(); - const loginUri = new LoginUriView(); - loginUri.uri = queueMessage.uri; - loginModel.uris = [loginUri]; - loginModel.username = queueMessage.username; - loginModel.password = queueMessage.password; - const model = new CipherView(); - model.name = Utils.getHostname(queueMessage.uri) || queueMessage.domain; - model.name = model.name.replace(/^www\./, ""); - model.type = CipherType.Login; - model.login = loginModel; + private async updatePassword( + cipherView: CipherView, + newPassword: string, + edit: boolean, + tab: chrome.tabs.Tab + ) { + cipherView.login.password = newPassword; - if (!Utils.isNullOrWhitespace(folderId)) { - const folders = await firstValueFrom(this.folderService.folderViews$); - if (folders.some((x) => x.id === folderId)) { - model.folderId = folderId; - } + if (edit) { + await this.editItem(cipherView, tab); + BrowserApi.tabSendMessage(tab, "editedCipher"); + return; } - const cipher = await this.cipherService.encrypt(model); - await this.cipherService.createWithServer(cipher); + const cipher = await this.cipherService.encrypt(cipherView); + await this.cipherService.updateWithServer(cipher); + // We've only updated the password, no need to broadcast editedCipher message + return; + } + + private async editItem(cipherView: CipherView, senderTab: chrome.tabs.Tab) { + await this.stateService.setAddEditCipherInfo({ + cipher: cipherView, + collectionIds: cipherView.collectionIds, + }); + + await BrowserApi.tabSendMessageData(senderTab, "openAddEditCipher", { + cipherId: cipherView.id, + }); + } + + private async folderExists(folderId: string) { + if (Utils.isNullOrWhitespace(folderId) || folderId === "null") { + return false; + } + + const folders = await firstValueFrom(this.folderService.folderViews$); + return folders.some((x) => x.id === folderId); } private async getDecryptedCipherById(cipherId: string) { @@ -419,14 +422,6 @@ export default class NotificationBackground { return null; } - private async updateCipher(cipher: CipherView, newPassword: string) { - if (cipher != null && cipher.type === CipherType.Login) { - cipher.login.password = newPassword; - const newCipher = await this.cipherService.encrypt(cipher); - await this.cipherService.updateWithServer(newCipher); - } - } - private async saveNever(tab: chrome.tabs.Tab) { for (let i = this.notificationQueue.length - 1; i >= 0; i--) { const queueMessage = this.notificationQueue[i]; @@ -459,9 +454,9 @@ export default class NotificationBackground { await BrowserApi.tabSendMessageData(tab, responseCommand, responseData); } - private async allowPersonalOwnership(): Promise { - return !(await firstValueFrom( + private async removeIndividualVault(): Promise { + return await firstValueFrom( this.policyService.policyAppliesToActiveUser$(PolicyType.PersonalOwnership) - )); + ); } } diff --git a/apps/browser/src/autofill/content/message_handler.ts b/apps/browser/src/autofill/content/message_handler.ts index 0f690bd4f79..5302a5042be 100644 --- a/apps/browser/src/autofill/content/message_handler.ts +++ b/apps/browser/src/autofill/content/message_handler.ts @@ -31,6 +31,7 @@ const forwardCommands = [ "addToLockedVaultPendingNotifications", "unlockCompleted", "addedCipher", + "openAddEditCipher", ]; chrome.runtime.onMessage.addListener((event) => { diff --git a/apps/browser/src/autofill/content/notification-bar.ts b/apps/browser/src/autofill/content/notification-bar.ts index 5a6d519b75e..c031929988d 100644 --- a/apps/browser/src/autofill/content/notification-bar.ts +++ b/apps/browser/src/autofill/content/notification-bar.ts @@ -502,6 +502,7 @@ document.addEventListener("DOMContentLoaded", (event) => { type, isVaultLocked: typeData.isVaultLocked, theme: typeData.theme, + removeIndividualVault: typeData.removeIndividualVault, }; const barQueryString = new URLSearchParams(barQueryParams).toString(); const barPage = "notification/bar.html?" + barQueryString; diff --git a/apps/browser/src/autofill/notification/bar.html b/apps/browser/src/autofill/notification/bar.html index 6f5b111f040..deec7fd512c 100644 --- a/apps/browser/src/autofill/notification/bar.html +++ b/apps/browser/src/autofill/notification/bar.html @@ -28,21 +28,27 @@ -