mirror of
https://github.com/bitwarden/browser.git
synced 2024-11-11 10:10:25 +01:00
[PM-10079] Add keyboard shortcut to autofill identity and credit cards (#10254)
* [BEEEP] Autofill Identity and Card Ciphers From Keyboard Shortcut * [PM-10079] Add keyboard shortcut to autofill identity and credit card ciphers * [PM-10079] Fixing jest tests * [PM-10079] Added an enum for the autofill commands, and adjusted how we filter out cipher types before sorting them by last used when calling for ID and card ciphers * [PM-10079] Updating copywriting for the autofill settings revolving around keyboard shortcuts * [PM-10079] Setting a method within CipherService as private
This commit is contained in:
parent
85c8ff04a1
commit
86acca3bec
@ -1343,8 +1343,14 @@
|
||||
"commandOpenSidebar": {
|
||||
"message": "Open vault in sidebar"
|
||||
},
|
||||
"commandAutofillDesc": {
|
||||
"message": "Auto-fill the last used login for the current website"
|
||||
"commandAutofillLoginDesc": {
|
||||
"message": "Autofill the last used login for the current website"
|
||||
},
|
||||
"commandAutofillCardDesc": {
|
||||
"message": "Autofill the last used card for the current website"
|
||||
},
|
||||
"commandAutofillIdentityDesc": {
|
||||
"message": "Autofill the last used identity for the current website"
|
||||
},
|
||||
"commandGeneratePasswordDesc": {
|
||||
"message": "Generate and copy a new random password to the clipboard"
|
||||
@ -2774,14 +2780,17 @@
|
||||
"autofillKeyboardShortcutUpdateLabel": {
|
||||
"message": "Change shortcut"
|
||||
},
|
||||
"autofillKeyboardManagerShortcutsLabel": {
|
||||
"message": "Manage shortcuts"
|
||||
},
|
||||
"autofillShortcut": {
|
||||
"message": "Autofill keyboard shortcut"
|
||||
},
|
||||
"autofillShortcutNotSet": {
|
||||
"message": "The autofill shortcut is not set. Change this in the browser's settings."
|
||||
"autofillLoginShortcutNotSet": {
|
||||
"message": "The autofill login shortcut is not set. Change this in the browser's settings."
|
||||
},
|
||||
"autofillShortcutText": {
|
||||
"message": "The autofill shortcut is: $COMMAND$. Change this in the browser's settings.",
|
||||
"autofillLoginShortcutText": {
|
||||
"message": "The autofill login shortcut is $COMMAND$. Manage all shortcuts in the browser's settings.",
|
||||
"placeholders": {
|
||||
"command": {
|
||||
"content": "$1",
|
||||
|
@ -4,6 +4,7 @@ import { BehaviorSubject, firstValueFrom } from "rxjs";
|
||||
import { PolicyService } from "@bitwarden/common/admin-console/services/policy/policy.service";
|
||||
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
|
||||
import { AuthService } from "@bitwarden/common/auth/services/auth.service";
|
||||
import { ExtensionCommand } from "@bitwarden/common/autofill/constants";
|
||||
import { DomainSettingsService } from "@bitwarden/common/autofill/services/domain-settings.service";
|
||||
import { UserNotificationSettingsService } from "@bitwarden/common/autofill/services/user-notification-settings.service";
|
||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||
@ -151,7 +152,7 @@ describe("NotificationBackground", () => {
|
||||
const message: NotificationBackgroundExtensionMessage = {
|
||||
command: "unlockCompleted",
|
||||
data: {
|
||||
commandToRetry: { message: { command: "autofill_login" } },
|
||||
commandToRetry: { message: { command: ExtensionCommand.AutofillLogin } },
|
||||
} as LockedVaultPendingNotificationsData,
|
||||
};
|
||||
jest.spyOn(BrowserApi, "tabSendMessageData").mockImplementation();
|
||||
|
@ -4,7 +4,11 @@ import { PolicyService } from "@bitwarden/common/admin-console/abstractions/poli
|
||||
import { PolicyType } from "@bitwarden/common/admin-console/enums";
|
||||
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
|
||||
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
|
||||
import { NOTIFICATION_BAR_LIFESPAN_MS } from "@bitwarden/common/autofill/constants";
|
||||
import {
|
||||
ExtensionCommand,
|
||||
ExtensionCommandType,
|
||||
NOTIFICATION_BAR_LIFESPAN_MS,
|
||||
} from "@bitwarden/common/autofill/constants";
|
||||
import { DomainSettingsService } from "@bitwarden/common/autofill/services/domain-settings.service";
|
||||
import { UserNotificationSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/user-notification-settings.service";
|
||||
import { NeverDomains } from "@bitwarden/common/models/domain/domain-service";
|
||||
@ -45,6 +49,11 @@ export default class NotificationBackground {
|
||||
private openUnlockPopout = openUnlockPopout;
|
||||
private openAddEditVaultItemPopout = openAddEditVaultItemPopout;
|
||||
private notificationQueue: NotificationQueueMessageItem[] = [];
|
||||
private allowedRetryCommands: Set<ExtensionCommandType> = new Set([
|
||||
ExtensionCommand.AutofillLogin,
|
||||
ExtensionCommand.AutofillCard,
|
||||
ExtensionCommand.AutofillIdentity,
|
||||
]);
|
||||
private readonly extensionMessageHandlers: NotificationBackgroundExtensionMessageHandlers = {
|
||||
unlockCompleted: ({ message, sender }) => this.handleUnlockCompleted(message, sender),
|
||||
bgGetFolderData: () => this.getFolderData(),
|
||||
@ -689,8 +698,8 @@ export default class NotificationBackground {
|
||||
sender: chrome.runtime.MessageSender,
|
||||
): Promise<void> {
|
||||
const messageData = message.data as LockedVaultPendingNotificationsData;
|
||||
const retryCommand = messageData.commandToRetry.message.command;
|
||||
if (retryCommand === "autofill_login") {
|
||||
const retryCommand = messageData.commandToRetry.message.command as ExtensionCommandType;
|
||||
if (this.allowedRetryCommands.has(retryCommand)) {
|
||||
await BrowserApi.tabSendMessageData(sender.tab, "closeNotificationBar");
|
||||
}
|
||||
|
||||
|
@ -16,6 +16,7 @@ import {
|
||||
CREATE_CARD_ID,
|
||||
CREATE_IDENTITY_ID,
|
||||
CREATE_LOGIN_ID,
|
||||
ExtensionCommand,
|
||||
GENERATE_PASSWORD_ID,
|
||||
NOOP_COMMAND_SUFFIX,
|
||||
} from "@bitwarden/common/autofill/constants";
|
||||
@ -79,7 +80,7 @@ export class ContextMenuClickedHandler {
|
||||
if ((await this.authService.getAuthStatus()) < AuthenticationStatus.Unlocked) {
|
||||
const retryMessage: LockedVaultPendingNotificationsData = {
|
||||
commandToRetry: {
|
||||
message: { command: NOOP_COMMAND_SUFFIX, contextMenuOnClickData: info },
|
||||
message: { command: ExtensionCommand.NoopCommand, contextMenuOnClickData: info },
|
||||
sender: { tab: tab },
|
||||
},
|
||||
target: "contextmenus.background",
|
||||
|
@ -159,9 +159,9 @@ export class AutofillV1Component implements OnInit {
|
||||
|
||||
private async setAutofillKeyboardHelperText(command: string) {
|
||||
if (command) {
|
||||
this.autofillKeyboardHelperText = this.i18nService.t("autofillShortcutText", command);
|
||||
this.autofillKeyboardHelperText = this.i18nService.t("autofillLoginShortcutText", command);
|
||||
} else {
|
||||
this.autofillKeyboardHelperText = this.i18nService.t("autofillShortcutNotSet");
|
||||
this.autofillKeyboardHelperText = this.i18nService.t("autofillLoginShortcutNotSet");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -107,7 +107,7 @@
|
||||
</bit-section-header>
|
||||
<bit-item>
|
||||
<button bit-item-content type="button" (click)="openURI($event, browserShortcutsURI)">
|
||||
<h3 bitTypography="h5">{{ "autofillKeyboardShortcutUpdateLabel" | i18n }}</h3>
|
||||
<h3 bitTypography="h5">{{ "autofillKeyboardManagerShortcutsLabel" | i18n }}</h3>
|
||||
<bit-hint slot="secondary" class="tw-text-sm tw-whitespace-normal">
|
||||
{{ autofillKeyboardHelperText }}
|
||||
</bit-hint>
|
||||
|
@ -215,9 +215,9 @@ export class AutofillComponent implements OnInit {
|
||||
|
||||
private async setAutofillKeyboardHelperText(command: string) {
|
||||
if (command) {
|
||||
this.autofillKeyboardHelperText = this.i18nService.t("autofillShortcutText", command);
|
||||
this.autofillKeyboardHelperText = this.i18nService.t("autofillLoginShortcutText", command);
|
||||
} else {
|
||||
this.autofillKeyboardHelperText = this.i18nService.t("autofillShortcutNotSet");
|
||||
this.autofillKeyboardHelperText = this.i18nService.t("autofillLoginShortcutNotSet");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1232,22 +1232,21 @@ describe("AutofillService", () => {
|
||||
jest.spyOn(autofillService as any, "getActiveTab").mockResolvedValueOnce(tab);
|
||||
jest.spyOn(autofillService, "doAutoFill").mockImplementation();
|
||||
jest
|
||||
.spyOn(autofillService["cipherService"], "getAllDecryptedForUrl")
|
||||
.mockResolvedValueOnce([cardCipher]);
|
||||
.spyOn(autofillService["cipherService"], "getNextCardCipher")
|
||||
.mockResolvedValueOnce(cardCipher);
|
||||
|
||||
await autofillService.doAutoFillActiveTab(cardFormPageDetails, false, CipherType.Card);
|
||||
await autofillService.doAutoFillActiveTab(cardFormPageDetails, true, CipherType.Card);
|
||||
|
||||
expect(autofillService["cipherService"].getAllDecryptedForUrl).toHaveBeenCalled();
|
||||
expect(autofillService.doAutoFill).toHaveBeenCalledWith({
|
||||
tab: tab,
|
||||
cipher: cardCipher,
|
||||
pageDetails: cardFormPageDetails,
|
||||
skipLastUsed: true,
|
||||
skipUsernameOnlyFill: true,
|
||||
onlyEmptyFields: true,
|
||||
onlyVisibleFields: true,
|
||||
skipLastUsed: false,
|
||||
skipUsernameOnlyFill: false,
|
||||
onlyEmptyFields: false,
|
||||
onlyVisibleFields: false,
|
||||
fillNewPassword: false,
|
||||
allowUntrustedIframe: false,
|
||||
allowUntrustedIframe: true,
|
||||
allowTotpAutofill: false,
|
||||
});
|
||||
});
|
||||
@ -1280,26 +1279,21 @@ describe("AutofillService", () => {
|
||||
jest.spyOn(autofillService as any, "getActiveTab").mockResolvedValueOnce(tab);
|
||||
jest.spyOn(autofillService, "doAutoFill").mockImplementation();
|
||||
jest
|
||||
.spyOn(autofillService["cipherService"], "getAllDecryptedForUrl")
|
||||
.mockResolvedValueOnce([identityCipher]);
|
||||
.spyOn(autofillService["cipherService"], "getNextIdentityCipher")
|
||||
.mockResolvedValueOnce(identityCipher);
|
||||
|
||||
await autofillService.doAutoFillActiveTab(
|
||||
identityFormPageDetails,
|
||||
false,
|
||||
CipherType.Identity,
|
||||
);
|
||||
await autofillService.doAutoFillActiveTab(identityFormPageDetails, true, CipherType.Identity);
|
||||
|
||||
expect(autofillService["cipherService"].getAllDecryptedForUrl).toHaveBeenCalled();
|
||||
expect(autofillService.doAutoFill).toHaveBeenCalledWith({
|
||||
tab: tab,
|
||||
cipher: identityCipher,
|
||||
pageDetails: identityFormPageDetails,
|
||||
skipLastUsed: true,
|
||||
skipUsernameOnlyFill: true,
|
||||
onlyEmptyFields: true,
|
||||
onlyVisibleFields: true,
|
||||
skipLastUsed: false,
|
||||
skipUsernameOnlyFill: false,
|
||||
onlyEmptyFields: false,
|
||||
onlyVisibleFields: false,
|
||||
fillNewPassword: false,
|
||||
allowUntrustedIframe: false,
|
||||
allowUntrustedIframe: true,
|
||||
allowTotpAutofill: false,
|
||||
});
|
||||
});
|
||||
|
@ -520,16 +520,30 @@ export default class AutofillService implements AutofillServiceInterface {
|
||||
return await this.doAutoFillOnTab(pageDetails, tab, fromCommand);
|
||||
}
|
||||
|
||||
// Cipher is a non-login type
|
||||
const cipher: CipherView = (
|
||||
(await this.cipherService.getAllDecryptedForUrl(tab.url, [cipherType])) || []
|
||||
).find(({ type }) => type === cipherType);
|
||||
let cipher: CipherView;
|
||||
let cacheKey = "";
|
||||
|
||||
if (!cipher || cipher.reprompt !== CipherRepromptType.None) {
|
||||
if (cipherType === CipherType.Card) {
|
||||
cacheKey = "cardCiphers";
|
||||
cipher = await this.cipherService.getNextCardCipher();
|
||||
} else {
|
||||
cacheKey = "identityCiphers";
|
||||
cipher = await this.cipherService.getNextIdentityCipher();
|
||||
}
|
||||
|
||||
if (!cipher || !cacheKey || (cipher.reprompt === CipherRepromptType.Password && !fromCommand)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return await this.doAutoFill({
|
||||
if (await this.isPasswordRepromptRequired(cipher, tab)) {
|
||||
if (fromCommand) {
|
||||
this.cipherService.updateLastUsedIndexForUrl(cacheKey);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
const totpCode = await this.doAutoFill({
|
||||
tab: tab,
|
||||
cipher: cipher,
|
||||
pageDetails: pageDetails,
|
||||
@ -541,6 +555,12 @@ export default class AutofillService implements AutofillServiceInterface {
|
||||
allowUntrustedIframe: fromCommand,
|
||||
allowTotpAutofill: false,
|
||||
});
|
||||
|
||||
if (fromCommand) {
|
||||
this.cipherService.updateLastUsedIndexForUrl(cacheKey);
|
||||
}
|
||||
|
||||
return totpCode;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { VaultTimeoutService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout.service";
|
||||
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
|
||||
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
|
||||
import { ExtensionCommand, ExtensionCommandType } from "@bitwarden/common/autofill/constants";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { PasswordGenerationServiceAbstraction } from "@bitwarden/generator-legacy";
|
||||
|
||||
@ -47,8 +48,23 @@ export default class CommandsBackground {
|
||||
case "generate_password":
|
||||
await this.generatePasswordToClipboard();
|
||||
break;
|
||||
case "autofill_login":
|
||||
await this.autoFillLogin(sender ? sender.tab : null);
|
||||
case ExtensionCommand.AutofillLogin:
|
||||
await this.triggerAutofillCommand(
|
||||
sender ? sender.tab : null,
|
||||
ExtensionCommand.AutofillCommand,
|
||||
);
|
||||
break;
|
||||
case ExtensionCommand.AutofillCard:
|
||||
await this.triggerAutofillCommand(
|
||||
sender ? sender.tab : null,
|
||||
ExtensionCommand.AutofillCard,
|
||||
);
|
||||
break;
|
||||
case ExtensionCommand.AutofillIdentity:
|
||||
await this.triggerAutofillCommand(
|
||||
sender ? sender.tab : null,
|
||||
ExtensionCommand.AutofillIdentity,
|
||||
);
|
||||
break;
|
||||
case "open_popup":
|
||||
await this.openPopup();
|
||||
@ -68,19 +84,27 @@ export default class CommandsBackground {
|
||||
await this.passwordGenerationService.addHistory(password);
|
||||
}
|
||||
|
||||
private async autoFillLogin(tab?: chrome.tabs.Tab) {
|
||||
private async triggerAutofillCommand(
|
||||
tab?: chrome.tabs.Tab,
|
||||
commandSender?: ExtensionCommandType,
|
||||
) {
|
||||
if (!tab) {
|
||||
tab = await BrowserApi.getTabFromCurrentWindowId();
|
||||
}
|
||||
|
||||
if (tab == null) {
|
||||
if (tab == null || !commandSender) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ((await this.authService.getAuthStatus()) < AuthenticationStatus.Unlocked) {
|
||||
const retryMessage: LockedVaultPendingNotificationsData = {
|
||||
commandToRetry: {
|
||||
message: { command: "autofill_login" },
|
||||
message: {
|
||||
command:
|
||||
commandSender === ExtensionCommand.AutofillCommand
|
||||
? ExtensionCommand.AutofillLogin
|
||||
: commandSender,
|
||||
},
|
||||
sender: { tab: tab },
|
||||
},
|
||||
target: "commands.background",
|
||||
@ -95,7 +119,7 @@ export default class CommandsBackground {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.main.collectPageDetailsForContentScript(tab, "autofill_cmd");
|
||||
await this.main.collectPageDetailsForContentScript(tab, commandSender);
|
||||
}
|
||||
|
||||
private async openPopup() {
|
||||
|
@ -2,7 +2,7 @@ import { firstValueFrom, map, mergeMap } from "rxjs";
|
||||
|
||||
import { NotificationsService } from "@bitwarden/common/abstractions/notifications.service";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { AutofillOverlayVisibility } from "@bitwarden/common/autofill/constants";
|
||||
import { AutofillOverlayVisibility, ExtensionCommand } from "@bitwarden/common/autofill/constants";
|
||||
import { AutofillSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/autofill-settings.service";
|
||||
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||
@ -117,7 +117,7 @@ export default class RuntimeBackground {
|
||||
case "collectPageDetailsResponse":
|
||||
switch (msg.sender) {
|
||||
case "autofiller":
|
||||
case "autofill_cmd": {
|
||||
case ExtensionCommand.AutofillCommand: {
|
||||
const activeUserId = await firstValueFrom(
|
||||
this.accountService.activeAccount$.pipe(map((a) => a?.id)),
|
||||
);
|
||||
@ -130,14 +130,14 @@ export default class RuntimeBackground {
|
||||
details: msg.details,
|
||||
},
|
||||
],
|
||||
msg.sender === "autofill_cmd",
|
||||
msg.sender === ExtensionCommand.AutofillCommand,
|
||||
);
|
||||
if (totpCode != null) {
|
||||
this.platformUtilsService.copyToClipboard(totpCode);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "autofill_card": {
|
||||
case ExtensionCommand.AutofillCard: {
|
||||
await this.autofillService.doAutoFillActiveTab(
|
||||
[
|
||||
{
|
||||
@ -146,12 +146,12 @@ export default class RuntimeBackground {
|
||||
details: msg.details,
|
||||
},
|
||||
],
|
||||
false,
|
||||
msg.sender === ExtensionCommand.AutofillCard,
|
||||
CipherType.Card,
|
||||
);
|
||||
break;
|
||||
}
|
||||
case "autofill_identity": {
|
||||
case ExtensionCommand.AutofillIdentity: {
|
||||
await this.autofillService.doAutoFillActiveTab(
|
||||
[
|
||||
{
|
||||
@ -160,7 +160,7 @@ export default class RuntimeBackground {
|
||||
details: msg.details,
|
||||
},
|
||||
],
|
||||
false,
|
||||
msg.sender === ExtensionCommand.AutofillIdentity,
|
||||
CipherType.Identity,
|
||||
);
|
||||
break;
|
||||
|
@ -94,7 +94,13 @@
|
||||
"suggested_key": {
|
||||
"default": "Ctrl+Shift+L"
|
||||
},
|
||||
"description": "__MSG_commandAutofillDesc__"
|
||||
"description": "__MSG_commandAutofillLoginDesc__"
|
||||
},
|
||||
"autofill_card": {
|
||||
"description": "__MSG_commandAutofillCardDesc__"
|
||||
},
|
||||
"autofill_identity": {
|
||||
"description": "__MSG_commandAutofillIdentityDesc__"
|
||||
},
|
||||
"generate_password": {
|
||||
"suggested_key": {
|
||||
|
@ -99,7 +99,13 @@
|
||||
"suggested_key": {
|
||||
"default": "Ctrl+Shift+L"
|
||||
},
|
||||
"description": "__MSG_commandAutofillDesc__"
|
||||
"description": "__MSG_commandAutofillLoginDesc__"
|
||||
},
|
||||
"autofill_card": {
|
||||
"description": "__MSG_commandAutofillCardDesc__"
|
||||
},
|
||||
"autofill_identity": {
|
||||
"description": "__MSG_commandAutofillIdentityDesc__"
|
||||
},
|
||||
"generate_password": {
|
||||
"suggested_key": {
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { ExtensionCommand } from "@bitwarden/common/autofill/constants";
|
||||
import { ClientType, DeviceType } from "@bitwarden/common/enums";
|
||||
import {
|
||||
ClipboardOptions,
|
||||
@ -298,7 +299,7 @@ export abstract class BrowserPlatformUtilsService implements PlatformUtilsServic
|
||||
autofillCommand = "Cmd+Shift+L";
|
||||
} else if (this.isFirefox()) {
|
||||
autofillCommand = (await browser.commands.getAll()).find(
|
||||
(c) => c.name === "autofill_login",
|
||||
(c) => c.name === ExtensionCommand.AutofillLogin,
|
||||
).shortcut;
|
||||
// Firefox is returning Ctrl instead of Cmd for the modifier key on macOS if
|
||||
// the command is the default one set on installation.
|
||||
@ -311,7 +312,9 @@ export abstract class BrowserPlatformUtilsService implements PlatformUtilsServic
|
||||
} else {
|
||||
await new Promise((resolve) =>
|
||||
chrome.commands.getAll((c) =>
|
||||
resolve((autofillCommand = c.find((c) => c.name === "autofill_login").shortcut)),
|
||||
resolve(
|
||||
(autofillCommand = c.find((c) => c.name === ExtensionCommand.AutofillLogin).shortcut),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -85,3 +85,17 @@ export const DisablePasswordManagerUris = {
|
||||
Vivaldi: "vivaldi://settings/autofill",
|
||||
Unknown: "https://bitwarden.com/help/disable-browser-autofill/",
|
||||
} as const;
|
||||
|
||||
export const ExtensionCommand = {
|
||||
AutofillCommand: "autofill_cmd",
|
||||
AutofillCard: "autofill_card",
|
||||
AutofillIdentity: "autofill_identity",
|
||||
AutofillLogin: "autofill_login",
|
||||
OpenAutofillOverlay: "open_autofill_overlay",
|
||||
GeneratePassword: "generate_password",
|
||||
OpenPopup: "open_popup",
|
||||
LockVault: "lock_vault",
|
||||
NoopCommand: "noop",
|
||||
} as const;
|
||||
|
||||
export type ExtensionCommandType = (typeof ExtensionCommand)[keyof typeof ExtensionCommand];
|
||||
|
@ -162,4 +162,6 @@ export abstract class CipherService implements UserKeyRotationDataProvider<Ciphe
|
||||
newUserKey: UserKey,
|
||||
userId: UserId,
|
||||
) => Promise<CipherWithIdRequest[]>;
|
||||
getNextCardCipher: () => Promise<CipherView>;
|
||||
getNextIdentityCipher: () => Promise<CipherView>;
|
||||
}
|
||||
|
@ -500,6 +500,13 @@ export class CipherService implements CipherServiceAbstraction {
|
||||
});
|
||||
}
|
||||
|
||||
private async getAllDecryptedCiphersOfType(type: CipherType[]): Promise<CipherView[]> {
|
||||
const ciphers = await this.getAllDecrypted();
|
||||
return ciphers
|
||||
.filter((cipher) => cipher.deletedDate == null && type.includes(cipher.type))
|
||||
.sort((a, b) => this.sortCiphersByLastUsedThenName(a, b));
|
||||
}
|
||||
|
||||
async getAllFromApiForOrganization(organizationId: string): Promise<CipherView[]> {
|
||||
const response = await this.apiService.getCiphersOrganization(organizationId);
|
||||
return await this.decryptOrganizationCiphersResponse(response, organizationId);
|
||||
@ -549,6 +556,36 @@ export class CipherService implements CipherServiceAbstraction {
|
||||
return this.getCipherForUrl(url, false, false, false);
|
||||
}
|
||||
|
||||
async getNextCardCipher(): Promise<CipherView> {
|
||||
const cacheKey = "cardCiphers";
|
||||
|
||||
if (!this.sortedCiphersCache.isCached(cacheKey)) {
|
||||
const ciphers = await this.getAllDecryptedCiphersOfType([CipherType.Card]);
|
||||
if (!ciphers?.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
this.sortedCiphersCache.addCiphers(cacheKey, ciphers);
|
||||
}
|
||||
|
||||
return this.sortedCiphersCache.getNext(cacheKey);
|
||||
}
|
||||
|
||||
async getNextIdentityCipher() {
|
||||
const cacheKey = "identityCiphers";
|
||||
|
||||
if (!this.sortedCiphersCache.isCached(cacheKey)) {
|
||||
const ciphers = await this.getAllDecryptedCiphersOfType([CipherType.Identity]);
|
||||
if (!ciphers?.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
this.sortedCiphersCache.addCiphers(cacheKey, ciphers);
|
||||
}
|
||||
|
||||
return this.sortedCiphersCache.getNext(cacheKey);
|
||||
}
|
||||
|
||||
updateLastUsedIndexForUrl(url: string) {
|
||||
this.sortedCiphersCache.updateLastUsedIndex(url);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user