convert password generator to jslib

This commit is contained in:
Kyle Spearrin 2018-01-09 17:55:28 -05:00
parent 79aca025d0
commit eb031eda0f
4 changed files with 10 additions and 250 deletions

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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<PasswordHistory[]>(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<PasswordHistory>();
}
async addHistory(password: string): Promise<any> {
// 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<any> {
this.history = [];
return await this.storageService.remove(Keys.history);
}
private async encryptHistory(): Promise<PasswordHistory[]> {
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<PasswordHistory[]> {
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;
}
}