diff --git a/src/background/commands.background.ts b/src/background/commands.background.ts index 9d956f632c..4c85c71eb4 100644 --- a/src/background/commands.background.ts +++ b/src/background/commands.background.ts @@ -2,7 +2,7 @@ import BrowserApi from '../browser/browserApi'; import MainBackground from './main.background'; -import PasswordGenerationService from '../services/passwordGeneration.service'; +import { PasswordGenerationService } from 'jslib/abstractions'; import { UtilsService } from 'jslib/services/utils.service'; @@ -34,7 +34,7 @@ export default class CommandsBackground { private async generatePasswordToClipboard() { const options = await this.passwordGenerationService.getOptions(); - const password = PasswordGenerationService.generatePassword(options); + const password = await this.passwordGenerationService.generatePassword(options); UtilsService.copyToClipboard(password); this.passwordGenerationService.addHistory(password); diff --git a/src/background/contextMenus.background.ts b/src/background/contextMenus.background.ts index 78b065b6b9..b55e0454aa 100644 --- a/src/background/contextMenus.background.ts +++ b/src/background/contextMenus.background.ts @@ -3,7 +3,10 @@ import BrowserApi from '../browser/browserApi'; import MainBackground from './main.background'; import CipherService from '../services/cipher.service'; -import PasswordGenerationService from '../services/passwordGeneration.service'; + +import { + PasswordGenerationService, +} from 'jslib/abstractions'; import { UtilsService } from 'jslib/services/utils.service'; @@ -32,7 +35,7 @@ export default class ContextMenusBackground { private async generatePasswordToClipboard() { const options = await this.passwordGenerationService.getOptions(); - const password = PasswordGenerationService.generatePassword(options); + const password = this.passwordGenerationService.generatePassword(options); UtilsService.copyToClipboard(password); this.passwordGenerationService.addHistory(password); diff --git a/src/background/main.background.ts b/src/background/main.background.ts index 4807f1afc7..0eafa94711 100644 --- a/src/background/main.background.ts +++ b/src/background/main.background.ts @@ -4,6 +4,7 @@ import { ApiService, AppIdService, CryptoService, + PasswordGenerationService, TokenService, TotpService, UserService, @@ -15,6 +16,7 @@ import { AppIdService as AppIdServiceAbstraction, CryptoService as CryptoServiceAbstraction, MessagingService as MessagingServiceAbstraction, + PasswordGenerationService as PasswordGenerationServiceAbstraction, PlatformUtilsService as PlatformUtilsServiceAbstraction, StorageService as StorageServiceAbstraction, TokenService as TokenServiceAbstraction, @@ -45,7 +47,6 @@ import EnvironmentService from '../services/environment.service'; import FolderService from '../services/folder.service'; import i18nService from '../services/i18n.service'; import LockService from '../services/lock.service'; -import PasswordGenerationService from '../services/passwordGeneration.service'; import SettingsService from '../services/settings.service'; import SyncService from '../services/sync.service'; @@ -68,7 +69,7 @@ export default class MainBackground { collectionService: CollectionService; lockService: LockService; syncService: SyncService; - passwordGenerationService: PasswordGenerationService; + passwordGenerationService: PasswordGenerationServiceAbstraction; totpService: TotpServiceAbstraction; autofillService: AutofillService; containerService: ContainerService; diff --git a/src/services/passwordGeneration.service.ts b/src/services/passwordGeneration.service.ts deleted file mode 100644 index 23a90dad99..0000000000 --- a/src/services/passwordGeneration.service.ts +++ /dev/null @@ -1,244 +0,0 @@ -import { - CipherString, - PasswordHistory, -} from 'jslib/models/domain'; - -import { UtilsService } from 'jslib/services/utils.service'; - -import { - CryptoService, - StorageService, -} from 'jslib/abstractions'; - -const DefaultOptions = { - length: 14, - ambiguous: false, - number: true, - minNumber: 1, - uppercase: true, - minUppercase: 1, - lowercase: true, - minLowercase: 1, - special: false, - minSpecial: 1, -}; - -const Keys = { - options: 'passwordGenerationOptions', - history: 'generatedPasswordHistory', -}; - -const MaxPasswordsInHistory = 100; - -export default class PasswordGenerationService { - static generatePassword(options: any): string { - // overload defaults with given options - const o = Object.assign({}, DefaultOptions, options); - - // sanitize - if (o.uppercase && o.minUppercase < 0) { - o.minUppercase = 1; - } - if (o.lowercase && o.minLowercase < 0) { - o.minLowercase = 1; - } - if (o.number && o.minNumber < 0) { - o.minNumber = 1; - } - if (o.special && o.minSpecial < 0) { - o.minSpecial = 1; - } - - if (!o.length || o.length < 1) { - o.length = 10; - } - - const minLength: number = o.minUppercase + o.minLowercase + o.minNumber + o.minSpecial; - if (o.length < minLength) { - o.length = minLength; - } - - const positions: string[] = []; - if (o.lowercase && o.minLowercase > 0) { - for (let i = 0; i < o.minLowercase; i++) { - positions.push('l'); - } - } - if (o.uppercase && o.minUppercase > 0) { - for (let i = 0; i < o.minUppercase; i++) { - positions.push('u'); - } - } - if (o.number && o.minNumber > 0) { - for (let i = 0; i < o.minNumber; i++) { - positions.push('n'); - } - } - if (o.special && o.minSpecial > 0) { - for (let i = 0; i < o.minSpecial; i++) { - positions.push('s'); - } - } - while (positions.length < o.length) { - positions.push('a'); - } - - // shuffle - positions.sort(() => { - return UtilsService.secureRandomNumber(0, 1) * 2 - 1; - }); - - // build out the char sets - let allCharSet = ''; - - let lowercaseCharSet = 'abcdefghijkmnopqrstuvwxyz'; - if (o.ambiguous) { - lowercaseCharSet += 'l'; - } - if (o.lowercase) { - allCharSet += lowercaseCharSet; - } - - let uppercaseCharSet = 'ABCDEFGHIJKLMNPQRSTUVWXYZ'; - if (o.ambiguous) { - uppercaseCharSet += 'O'; - } - if (o.uppercase) { - allCharSet += uppercaseCharSet; - } - - let numberCharSet = '23456789'; - if (o.ambiguous) { - numberCharSet += '01'; - } - if (o.number) { - allCharSet += numberCharSet; - } - - const specialCharSet = '!@#$%^&*'; - if (o.special) { - allCharSet += specialCharSet; - } - - let password = ''; - for (let i = 0; i < o.length; i++) { - let positionChars: string; - switch (positions[i]) { - case 'l': - positionChars = lowercaseCharSet; - break; - case 'u': - positionChars = uppercaseCharSet; - break; - case 'n': - positionChars = numberCharSet; - break; - case 's': - positionChars = specialCharSet; - break; - case 'a': - positionChars = allCharSet; - break; - } - - const randomCharIndex = UtilsService.secureRandomNumber(0, positionChars.length - 1); - password += positionChars.charAt(randomCharIndex); - } - - return password; - } - - optionsCache: any; - history: PasswordHistory[] = []; - - constructor(private cryptoService: CryptoService, - private storageService: StorageService) { - storageService.get(Keys.history).then((encrypted) => { - return this.decryptHistory(encrypted); - }).then((history) => { - this.history = history; - }); - } - - generatePassword(options: any) { - return PasswordGenerationService.generatePassword(options); - } - - async getOptions() { - if (this.optionsCache == null) { - const options = await this.storageService.get(Keys.options); - if (options == null) { - this.optionsCache = DefaultOptions; - } else { - this.optionsCache = options; - } - } - - return this.optionsCache; - } - - async saveOptions(options: any) { - await this.storageService.save(Keys.options, options); - this.optionsCache = options; - } - - getHistory() { - return this.history || new Array(); - } - - async addHistory(password: string): Promise { - // Prevent duplicates - if (this.matchesPrevious(password)) { - return; - } - - this.history.push(new PasswordHistory(password, Date.now())); - - // Remove old items. - if (this.history.length > MaxPasswordsInHistory) { - this.history.shift(); - } - - const newHistory = await this.encryptHistory(); - return await this.storageService.save(Keys.history, newHistory); - } - - async clear(): Promise { - this.history = []; - return await this.storageService.remove(Keys.history); - } - - private async encryptHistory(): Promise { - if (this.history == null || this.history.length === 0) { - return Promise.resolve([]); - } - - const promises = this.history.map(async (item) => { - const encrypted = await this.cryptoService.encrypt(item.password); - return new PasswordHistory(encrypted.encryptedString, item.date); - }); - - return await Promise.all(promises); - } - - private async decryptHistory(history: PasswordHistory[]): Promise { - if (history == null || history.length === 0) { - return Promise.resolve([]); - } - - const promises = history.map(async (item) => { - const decrypted = await this.cryptoService.decrypt(new CipherString(item.password)); - return new PasswordHistory(decrypted, item.date); - }); - - return await Promise.all(promises); - } - - private matchesPrevious(password: string): boolean { - if (this.history == null || this.history.length === 0) { - return false; - } - - return this.history[this.history.length - 1].password === password; - } -}