mirror of
https://github.com/aliasvault/aliasvault.git
synced 2026-03-19 23:28:23 -04:00
Add password generator settings awareness to browser extension (#167)
This commit is contained in:
@@ -2,7 +2,7 @@ import { browser } from "wxt/browser";
|
||||
import { defineBackground } from 'wxt/sandbox';
|
||||
import { onMessage } from "webext-bridge/background";
|
||||
import { setupContextMenus, handleContextMenuClick } from './background/ContextMenu';
|
||||
import { handleCheckAuthStatus, handleClearVault, handleCreateIdentity, handleGetCredentials, handleGetDefaultEmailDomain, handleGetDerivedKey, handleGetVault, handleStoreVault, handleSyncVault } from './background/VaultMessageHandler';
|
||||
import { handleCheckAuthStatus, handleClearVault, handleCreateIdentity, handleGetCredentials, handleGetDefaultEmailDomain, handleGetDerivedKey, handleGetPasswordSettings, handleGetVault, handleStoreVault, handleSyncVault } from './background/VaultMessageHandler';
|
||||
import { handleOpenPopup, handlePopupWithCredential } from './background/PopupMessageHandler';
|
||||
|
||||
export default defineBackground({
|
||||
@@ -12,7 +12,7 @@ export default defineBackground({
|
||||
main() {
|
||||
// Set up context menus
|
||||
setupContextMenus();
|
||||
browser.contextMenus.onClicked.addListener((info: browser.menus.OnClickData, tab?: browser.tabs.Tab) =>
|
||||
browser.contextMenus.onClicked.addListener((info: browser.contextMenus.OnClickData, tab?: browser.tabs.Tab) =>
|
||||
handleContextMenuClick(info, tab)
|
||||
);
|
||||
|
||||
@@ -25,6 +25,7 @@ export default defineBackground({
|
||||
onMessage('GET_CREDENTIALS', () => handleGetCredentials());
|
||||
onMessage('CREATE_IDENTITY', ({ data }) => handleCreateIdentity(data));
|
||||
onMessage('GET_DEFAULT_EMAIL_DOMAIN', () => handleGetDefaultEmailDomain());
|
||||
onMessage('GET_PASSWORD_SETTINGS', () => handleGetPasswordSettings());
|
||||
onMessage('GET_DERIVED_KEY', () => handleGetDerivedKey());
|
||||
onMessage('OPEN_POPUP', () => handleOpenPopup());
|
||||
onMessage('OPEN_POPUP_WITH_CREDENTIAL', ({ data }) => handlePopupWithCredential(data));
|
||||
|
||||
@@ -10,6 +10,7 @@ import { BoolResponse as messageBoolResponse } from '../../utils/types/messaging
|
||||
import { VaultResponse as messageVaultResponse } from '../../utils/types/messaging/VaultResponse';
|
||||
import { CredentialsResponse as messageCredentialsResponse } from '../../utils/types/messaging/CredentialsResponse';
|
||||
import { DefaultEmailDomainResponse as messageDefaultEmailDomainResponse } from '../../utils/types/messaging/DefaultEmailDomainResponse';
|
||||
import { PasswordSettingsResponse as messagePasswordSettingsResponse } from '../../utils/types/messaging/PasswordSettingsResponse';
|
||||
|
||||
/**
|
||||
* Check if the user is logged in and if the vault is locked.
|
||||
@@ -258,6 +259,22 @@ export function handleGetDefaultEmailDomain(
|
||||
})();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the password settings.
|
||||
*/
|
||||
export async function handleGetPasswordSettings(
|
||||
) : Promise<messagePasswordSettingsResponse> {
|
||||
try {
|
||||
const sqliteClient = await createVaultSqliteClient();
|
||||
const passwordSettings = sqliteClient.getPasswordSettings();
|
||||
|
||||
return { success: true, settings: passwordSettings };
|
||||
} catch (error) {
|
||||
console.error('Error getting password settings:', error);
|
||||
return { success: false, error: 'Failed to get password settings' };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the derived key for the encrypted vault.
|
||||
*/
|
||||
|
||||
@@ -7,6 +7,7 @@ import { storage } from "wxt/storage";
|
||||
import { sendMessage } from "webext-bridge/content-script";
|
||||
import { CredentialsResponse } from '@/utils/types/messaging/CredentialsResponse';
|
||||
import { CombinedStopWords } from '../../utils/formDetector/FieldPatterns';
|
||||
import { PasswordSettingsResponse } from '@/utils/types/messaging/PasswordSettingsResponse';
|
||||
|
||||
/**
|
||||
* WeakMap to store event listeners for popup containers
|
||||
@@ -212,7 +213,11 @@ export function createAutofillPopup(input: HTMLInputElement, credentials: Creden
|
||||
const identityGenerator = new IdentityGeneratorEn();
|
||||
const identity = await identityGenerator.generateRandomIdentity();
|
||||
|
||||
const passwordGenerator = new PasswordGenerator();
|
||||
// Get password settings from background
|
||||
const passwordSettingsResponse = await sendMessage('GET_PASSWORD_SETTINGS', {}, 'background') as PasswordSettingsResponse;
|
||||
|
||||
// Initialize password generator with the retrieved settings
|
||||
const passwordGenerator = new PasswordGenerator(passwordSettingsResponse.settings);
|
||||
const password = passwordGenerator.generateRandomPassword();
|
||||
|
||||
// Extract favicon from page and get the bytes
|
||||
|
||||
@@ -2,6 +2,7 @@ import initSqlJs, { Database } from 'sql.js';
|
||||
import { Credential } from './types/Credential';
|
||||
import { EncryptionKey } from './types/EncryptionKey';
|
||||
import { TotpCode } from './types/TotpCode';
|
||||
import { PasswordSettings } from './types/PasswordSettings';
|
||||
|
||||
/**
|
||||
* Client for interacting with the SQLite database.
|
||||
@@ -280,6 +281,33 @@ class SqliteClient {
|
||||
return this.getSetting('DefaultEmailDomain');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the password settings from the database.
|
||||
*/
|
||||
public getPasswordSettings(): PasswordSettings {
|
||||
const settingsJson = this.getSetting('PasswordGenerationSettings');
|
||||
|
||||
// Default settings if none found or parsing fails
|
||||
const defaultSettings: PasswordSettings = {
|
||||
Length: 18,
|
||||
UseLowercase: true,
|
||||
UseUppercase: true,
|
||||
UseNumbers: true,
|
||||
UseSpecialChars: true,
|
||||
UseNonAmbiguousChars: false
|
||||
};
|
||||
|
||||
try {
|
||||
if (settingsJson) {
|
||||
return { ...defaultSettings, ...JSON.parse(settingsJson) };
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('Failed to parse password settings:', error);
|
||||
}
|
||||
|
||||
return defaultSettings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new credential with associated entities
|
||||
* @param credential The credential object to insert
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { PasswordSettings } from '../../types/PasswordSettings';
|
||||
|
||||
/**
|
||||
* Generate a random password.
|
||||
*/
|
||||
@@ -6,12 +8,37 @@ export class PasswordGenerator {
|
||||
private readonly uppercaseChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
||||
private readonly numberChars = '0123456789';
|
||||
private readonly specialChars = '!@#$%^&*()_+-=[]{}|;:,.<>?';
|
||||
private readonly ambiguousChars = 'Il1O0';
|
||||
|
||||
private length: number = 18;
|
||||
private useLowercase: boolean = true;
|
||||
private useUppercase: boolean = true;
|
||||
private useNumbers: boolean = true;
|
||||
private useSpecial: boolean = true;
|
||||
private useNonAmbiguous: boolean = false;
|
||||
|
||||
/**
|
||||
* Create a new instance of PasswordGenerator
|
||||
* @param settings Optional password settings to initialize with
|
||||
*/
|
||||
public constructor(settings?: PasswordSettings) {
|
||||
if (settings) {
|
||||
this.applySettings(settings);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply password settings to this generator
|
||||
*/
|
||||
public applySettings(settings: PasswordSettings): this {
|
||||
this.length = settings.Length;
|
||||
this.useLowercase = settings.UseLowercase;
|
||||
this.useUppercase = settings.UseUppercase;
|
||||
this.useNumbers = settings.UseNumbers;
|
||||
this.useSpecial = settings.UseSpecialChars;
|
||||
this.useNonAmbiguous = settings.UseNonAmbiguousChars;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the length of the password.
|
||||
@@ -53,6 +80,14 @@ export class PasswordGenerator {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set if only non-ambiguous characters should be used.
|
||||
*/
|
||||
public useNonAmbiguousCharacters(use: boolean): this {
|
||||
this.useNonAmbiguous = use;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a random index from the crypto module.
|
||||
*/
|
||||
@@ -98,6 +133,13 @@ export class PasswordGenerator {
|
||||
chars = this.lowercaseChars;
|
||||
}
|
||||
|
||||
// Remove ambiguous characters if needed
|
||||
if (this.useNonAmbiguous) {
|
||||
for (const ambChar of this.ambiguousChars) {
|
||||
chars = chars.replace(ambChar, '');
|
||||
}
|
||||
}
|
||||
|
||||
// Generate password
|
||||
for (let i = 0; i < this.length; i++) {
|
||||
password += chars[this.getUnbiasedRandomIndex(chars.length)];
|
||||
@@ -105,21 +147,41 @@ export class PasswordGenerator {
|
||||
|
||||
// Ensure password contains at least one character from each selected set
|
||||
if (this.useLowercase && !/[a-z]/.exec(password)) {
|
||||
let lowercaseCharsToUse = this.lowercaseChars;
|
||||
if (this.useNonAmbiguous) {
|
||||
for (const ambChar of this.ambiguousChars) {
|
||||
lowercaseCharsToUse = lowercaseCharsToUse.replace(ambChar.toLowerCase(), '');
|
||||
}
|
||||
}
|
||||
const pos = this.getUnbiasedRandomIndex(this.length);
|
||||
password = password.substring(0, pos) +
|
||||
this.lowercaseChars[this.getUnbiasedRandomIndex(this.lowercaseChars.length)] +
|
||||
lowercaseCharsToUse[this.getUnbiasedRandomIndex(lowercaseCharsToUse.length)] +
|
||||
password.substring(pos + 1);
|
||||
}
|
||||
if (this.useUppercase && !/[A-Z]/.exec(password)) {
|
||||
let uppercaseCharsToUse = this.uppercaseChars;
|
||||
if (this.useNonAmbiguous) {
|
||||
for (const ambChar of this.ambiguousChars) {
|
||||
uppercaseCharsToUse = uppercaseCharsToUse.replace(ambChar.toUpperCase(), '');
|
||||
}
|
||||
}
|
||||
const pos = this.getUnbiasedRandomIndex(this.length);
|
||||
password = password.substring(0, pos) +
|
||||
this.uppercaseChars[this.getUnbiasedRandomIndex(this.uppercaseChars.length)] +
|
||||
uppercaseCharsToUse[this.getUnbiasedRandomIndex(uppercaseCharsToUse.length)] +
|
||||
password.substring(pos + 1);
|
||||
}
|
||||
if (this.useNumbers && !/\d/.exec(password)) {
|
||||
let numberCharsToUse = this.numberChars;
|
||||
if (this.useNonAmbiguous) {
|
||||
for (const ambChar of this.ambiguousChars) {
|
||||
if (/\d/.test(ambChar)) {
|
||||
numberCharsToUse = numberCharsToUse.replace(ambChar, '');
|
||||
}
|
||||
}
|
||||
}
|
||||
const pos = this.getUnbiasedRandomIndex(this.length);
|
||||
password = password.substring(0, pos) +
|
||||
this.numberChars[this.getUnbiasedRandomIndex(this.numberChars.length)] +
|
||||
numberCharsToUse[this.getUnbiasedRandomIndex(numberCharsToUse.length)] +
|
||||
password.substring(pos + 1);
|
||||
}
|
||||
if (this.useSpecial && !/[!@#$%^&*()_+\-=[\]{}|;:,.<>?]/.exec(password)) {
|
||||
|
||||
34
browser-extension/src/utils/types/PasswordSettings.ts
Normal file
34
browser-extension/src/utils/types/PasswordSettings.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Settings for password generation stored in SQLite database settings table as string.
|
||||
*/
|
||||
export type PasswordSettings = {
|
||||
/**
|
||||
* The length of the password.
|
||||
*/
|
||||
Length: number;
|
||||
|
||||
/**
|
||||
* Whether to use lowercase letters.
|
||||
*/
|
||||
UseLowercase: boolean;
|
||||
|
||||
/**
|
||||
* Whether to use uppercase letters.
|
||||
*/
|
||||
UseUppercase: boolean;
|
||||
|
||||
/**
|
||||
* Whether to use numbers.
|
||||
*/
|
||||
UseNumbers: boolean;
|
||||
|
||||
/**
|
||||
* Whether to use special characters.
|
||||
*/
|
||||
UseSpecialChars: boolean;
|
||||
|
||||
/**
|
||||
* Whether to use non-ambiguous characters.
|
||||
*/
|
||||
UseNonAmbiguousChars: boolean;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import { PasswordSettings } from "@/utils/types/PasswordSettings";
|
||||
|
||||
export type PasswordSettingsResponse = {
|
||||
success: boolean,
|
||||
error?: string,
|
||||
settings?: PasswordSettings
|
||||
};
|
||||
Reference in New Issue
Block a user