mirror of
https://github.com/bitwarden/browser.git
synced 2024-11-22 11:45:59 +01:00
Username generator (#734)
* add support for username generation * remove unused Router * pr feedback
This commit is contained in:
parent
5b7b2a03dd
commit
bfdd3561da
@ -47,6 +47,7 @@ export class AddEditComponent implements OnInit {
|
|||||||
@Output() onShareCipher = new EventEmitter<CipherView>();
|
@Output() onShareCipher = new EventEmitter<CipherView>();
|
||||||
@Output() onEditCollections = new EventEmitter<CipherView>();
|
@Output() onEditCollections = new EventEmitter<CipherView>();
|
||||||
@Output() onGeneratePassword = new EventEmitter();
|
@Output() onGeneratePassword = new EventEmitter();
|
||||||
|
@Output() onGenerateUsername = new EventEmitter();
|
||||||
|
|
||||||
editMode = false;
|
editMode = false;
|
||||||
cipher: CipherView;
|
cipher: CipherView;
|
||||||
@ -425,12 +426,25 @@ export class AddEditComponent implements OnInit {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async generateUsername(): Promise<boolean> {
|
||||||
|
if (this.cipher.login?.username?.length) {
|
||||||
|
const confirmed = await this.platformUtilsService.showDialog(
|
||||||
|
this.i18nService.t("overwriteUsernameConfirmation"),
|
||||||
|
this.i18nService.t("overwriteUsername"),
|
||||||
|
this.i18nService.t("yes"),
|
||||||
|
this.i18nService.t("no")
|
||||||
|
);
|
||||||
|
if (!confirmed) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.onGenerateUsername.emit();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
async generatePassword(): Promise<boolean> {
|
async generatePassword(): Promise<boolean> {
|
||||||
if (
|
if (this.cipher.login?.password?.length) {
|
||||||
this.cipher.login != null &&
|
|
||||||
this.cipher.login.password != null &&
|
|
||||||
this.cipher.login.password.length
|
|
||||||
) {
|
|
||||||
const confirmed = await this.platformUtilsService.showDialog(
|
const confirmed = await this.platformUtilsService.showDialog(
|
||||||
this.i18nService.t("overwritePasswordConfirmation"),
|
this.i18nService.t("overwritePasswordConfirmation"),
|
||||||
this.i18nService.t("overwritePassword"),
|
this.i18nService.t("overwritePassword"),
|
||||||
|
@ -1,97 +1,201 @@
|
|||||||
import { Directive, EventEmitter, Input, OnInit, Output } from "@angular/core";
|
import { Directive, EventEmitter, Input, OnInit, Output } from "@angular/core";
|
||||||
|
import { ActivatedRoute } from "@angular/router";
|
||||||
|
import { first } from "rxjs/operators";
|
||||||
|
|
||||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||||
import { PasswordGenerationService } from "jslib-common/abstractions/passwordGeneration.service";
|
import { PasswordGenerationService } from "jslib-common/abstractions/passwordGeneration.service";
|
||||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||||
|
import { StateService } from "jslib-common/abstractions/state.service";
|
||||||
|
import { UsernameGenerationService } from "jslib-common/abstractions/usernameGeneration.service";
|
||||||
import { PasswordGeneratorPolicyOptions } from "jslib-common/models/domain/passwordGeneratorPolicyOptions";
|
import { PasswordGeneratorPolicyOptions } from "jslib-common/models/domain/passwordGeneratorPolicyOptions";
|
||||||
|
|
||||||
@Directive()
|
@Directive()
|
||||||
export class PasswordGeneratorComponent implements OnInit {
|
export class PasswordGeneratorComponent implements OnInit {
|
||||||
@Input() showSelect = false;
|
@Input() showSelect = false;
|
||||||
|
@Input() type = "password";
|
||||||
@Output() onSelected = new EventEmitter<string>();
|
@Output() onSelected = new EventEmitter<string>();
|
||||||
|
|
||||||
|
typeOptions: any[];
|
||||||
passTypeOptions: any[];
|
passTypeOptions: any[];
|
||||||
options: any = {};
|
usernameTypeOptions: any[];
|
||||||
|
subaddressOptions: any[];
|
||||||
|
catchallOptions: any[];
|
||||||
|
forwardOptions: any[];
|
||||||
|
usernameOptions: any = {};
|
||||||
|
passwordOptions: any = {};
|
||||||
|
username = "-";
|
||||||
password = "-";
|
password = "-";
|
||||||
showOptions = false;
|
showOptions = false;
|
||||||
avoidAmbiguous = false;
|
avoidAmbiguous = false;
|
||||||
enforcedPolicyOptions: PasswordGeneratorPolicyOptions;
|
showWebsiteOption = false;
|
||||||
|
enforcedPasswordPolicyOptions: PasswordGeneratorPolicyOptions;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
protected passwordGenerationService: PasswordGenerationService,
|
protected passwordGenerationService: PasswordGenerationService,
|
||||||
|
protected usernameGenerationService: UsernameGenerationService,
|
||||||
protected platformUtilsService: PlatformUtilsService,
|
protected platformUtilsService: PlatformUtilsService,
|
||||||
|
protected stateService: StateService,
|
||||||
protected i18nService: I18nService,
|
protected i18nService: I18nService,
|
||||||
|
protected route: ActivatedRoute,
|
||||||
private win: Window
|
private win: Window
|
||||||
) {
|
) {
|
||||||
|
this.typeOptions = [
|
||||||
|
{ name: i18nService.t("password"), value: "password" },
|
||||||
|
{ name: i18nService.t("username"), value: "username" },
|
||||||
|
];
|
||||||
this.passTypeOptions = [
|
this.passTypeOptions = [
|
||||||
{ name: i18nService.t("password"), value: "password" },
|
{ name: i18nService.t("password"), value: "password" },
|
||||||
{ name: i18nService.t("passphrase"), value: "passphrase" },
|
{ name: i18nService.t("passphrase"), value: "passphrase" },
|
||||||
];
|
];
|
||||||
|
this.usernameTypeOptions = [
|
||||||
|
{
|
||||||
|
name: i18nService.t("plusAddressedEmail"),
|
||||||
|
value: "subaddress",
|
||||||
|
desc: i18nService.t("plusAddressedEmailDesc"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: i18nService.t("catchallEmail"),
|
||||||
|
value: "catchall",
|
||||||
|
desc: i18nService.t("catchallEmailDesc"),
|
||||||
|
},
|
||||||
|
{ name: i18nService.t("randomWord"), value: "word" },
|
||||||
|
];
|
||||||
|
this.subaddressOptions = [{ name: i18nService.t("random"), value: "random" }];
|
||||||
|
this.catchallOptions = [{ name: i18nService.t("random"), value: "random" }];
|
||||||
|
this.forwardOptions = [
|
||||||
|
{ name: "SimpleLogin", value: "simplelogin" },
|
||||||
|
{ name: "FastMail", value: "fastmail" },
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
const optionsResponse = await this.passwordGenerationService.getOptions();
|
this.route.queryParams.pipe(first()).subscribe(async (qParams) => {
|
||||||
this.options = optionsResponse[0];
|
const passwordOptionsResponse = await this.passwordGenerationService.getOptions();
|
||||||
this.enforcedPolicyOptions = optionsResponse[1];
|
this.passwordOptions = passwordOptionsResponse[0];
|
||||||
this.avoidAmbiguous = !this.options.ambiguous;
|
this.enforcedPasswordPolicyOptions = passwordOptionsResponse[1];
|
||||||
this.options.type = this.options.type === "passphrase" ? "passphrase" : "password";
|
this.avoidAmbiguous = !this.passwordOptions.ambiguous;
|
||||||
this.password = await this.passwordGenerationService.generatePassword(this.options);
|
this.passwordOptions.type =
|
||||||
await this.passwordGenerationService.addHistory(this.password);
|
this.passwordOptions.type === "passphrase" ? "passphrase" : "password";
|
||||||
|
|
||||||
|
if (this.showWebsiteOption) {
|
||||||
|
const websiteOption = { name: this.i18nService.t("websiteName"), value: "website-name" };
|
||||||
|
this.subaddressOptions.push(websiteOption);
|
||||||
|
this.catchallOptions.push(websiteOption);
|
||||||
|
}
|
||||||
|
this.usernameOptions = await this.usernameGenerationService.getOptions();
|
||||||
|
if (this.usernameOptions.type == null) {
|
||||||
|
this.usernameOptions.type = "word";
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
this.usernameOptions.subaddressEmail == null ||
|
||||||
|
this.usernameOptions.subaddressEmail === ""
|
||||||
|
) {
|
||||||
|
this.usernameOptions.subaddressEmail = await this.stateService.getEmail();
|
||||||
|
}
|
||||||
|
if (!this.showWebsiteOption) {
|
||||||
|
this.usernameOptions.subaddressType = this.usernameOptions.catchallType = "random";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (qParams.type === "username" || qParams.type === "password") {
|
||||||
|
this.type = qParams.type;
|
||||||
|
} else {
|
||||||
|
const generatorOptions = await this.stateService.getGeneratorOptions();
|
||||||
|
if (generatorOptions != null && generatorOptions.type != null) {
|
||||||
|
this.type = generatorOptions.type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await this.regenerate();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async typeChanged() {
|
||||||
|
await this.stateService.setGeneratorOptions({ type: this.type });
|
||||||
|
await this.regenerate();
|
||||||
|
}
|
||||||
|
|
||||||
|
async regenerate() {
|
||||||
|
if (this.type === "password") {
|
||||||
|
await this.regeneratePassword();
|
||||||
|
} else if (this.type === "username") {
|
||||||
|
await this.regenerateUsername();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async sliderChanged() {
|
async sliderChanged() {
|
||||||
this.saveOptions(false);
|
this.savePasswordOptions(false);
|
||||||
await this.passwordGenerationService.addHistory(this.password);
|
await this.passwordGenerationService.addHistory(this.password);
|
||||||
}
|
}
|
||||||
|
|
||||||
async sliderInput() {
|
async sliderInput() {
|
||||||
this.normalizeOptions();
|
this.normalizePasswordOptions();
|
||||||
this.password = await this.passwordGenerationService.generatePassword(this.options);
|
this.password = await this.passwordGenerationService.generatePassword(this.passwordOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
async saveOptions(regenerate = true) {
|
async savePasswordOptions(regenerate = true) {
|
||||||
this.normalizeOptions();
|
this.normalizePasswordOptions();
|
||||||
await this.passwordGenerationService.saveOptions(this.options);
|
await this.passwordGenerationService.saveOptions(this.passwordOptions);
|
||||||
|
|
||||||
if (regenerate) {
|
if (regenerate) {
|
||||||
await this.regenerate();
|
await this.regeneratePassword();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async regenerate() {
|
async saveUsernameOptions(regenerate = true) {
|
||||||
this.password = await this.passwordGenerationService.generatePassword(this.options);
|
await this.usernameGenerationService.saveOptions(this.usernameOptions);
|
||||||
|
if (regenerate) {
|
||||||
|
await this.regenerateUsername();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async regeneratePassword() {
|
||||||
|
this.password = await this.passwordGenerationService.generatePassword(this.passwordOptions);
|
||||||
await this.passwordGenerationService.addHistory(this.password);
|
await this.passwordGenerationService.addHistory(this.password);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
regenerateUsername() {
|
||||||
|
return this.generateUsername();
|
||||||
|
}
|
||||||
|
|
||||||
|
async generateUsername() {
|
||||||
|
this.username = await this.usernameGenerationService.generateUsername(this.usernameOptions);
|
||||||
|
if (this.username === "" || this.username === null) {
|
||||||
|
this.username = "-";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
copy() {
|
copy() {
|
||||||
|
const password = this.type === "password";
|
||||||
const copyOptions = this.win != null ? { window: this.win } : null;
|
const copyOptions = this.win != null ? { window: this.win } : null;
|
||||||
this.platformUtilsService.copyToClipboard(this.password, copyOptions);
|
this.platformUtilsService.copyToClipboard(
|
||||||
|
password ? this.password : this.username,
|
||||||
|
copyOptions
|
||||||
|
);
|
||||||
this.platformUtilsService.showToast(
|
this.platformUtilsService.showToast(
|
||||||
"info",
|
"info",
|
||||||
null,
|
null,
|
||||||
this.i18nService.t("valueCopied", this.i18nService.t("password"))
|
this.i18nService.t("valueCopied", this.i18nService.t(password ? "password" : "username"))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
select() {
|
select() {
|
||||||
this.onSelected.emit(this.password);
|
this.onSelected.emit(this.type === "password" ? this.password : this.username);
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleOptions() {
|
toggleOptions() {
|
||||||
this.showOptions = !this.showOptions;
|
this.showOptions = !this.showOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
private normalizeOptions() {
|
private normalizePasswordOptions() {
|
||||||
// Application level normalize options depedent on class variables
|
// Application level normalize options depedent on class variables
|
||||||
this.options.ambiguous = !this.avoidAmbiguous;
|
this.passwordOptions.ambiguous = !this.avoidAmbiguous;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!this.options.uppercase &&
|
!this.passwordOptions.uppercase &&
|
||||||
!this.options.lowercase &&
|
!this.passwordOptions.lowercase &&
|
||||||
!this.options.number &&
|
!this.passwordOptions.number &&
|
||||||
!this.options.special
|
!this.passwordOptions.special
|
||||||
) {
|
) {
|
||||||
this.options.lowercase = true;
|
this.passwordOptions.lowercase = true;
|
||||||
if (this.win != null) {
|
if (this.win != null) {
|
||||||
const lowercase = this.win.document.querySelector("#lowercase") as HTMLInputElement;
|
const lowercase = this.win.document.querySelector("#lowercase") as HTMLInputElement;
|
||||||
if (lowercase) {
|
if (lowercase) {
|
||||||
@ -100,6 +204,9 @@ export class PasswordGeneratorComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.passwordGenerationService.normalizeOptions(this.options, this.enforcedPolicyOptions);
|
this.passwordGenerationService.normalizeOptions(
|
||||||
|
this.passwordOptions,
|
||||||
|
this.enforcedPasswordPolicyOptions
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -265,6 +265,10 @@ export abstract class StateService<T extends Account = Account> {
|
|||||||
) => Promise<void>;
|
) => Promise<void>;
|
||||||
getPasswordGenerationOptions: (options?: StorageOptions) => Promise<any>;
|
getPasswordGenerationOptions: (options?: StorageOptions) => Promise<any>;
|
||||||
setPasswordGenerationOptions: (value: any, options?: StorageOptions) => Promise<void>;
|
setPasswordGenerationOptions: (value: any, options?: StorageOptions) => Promise<void>;
|
||||||
|
getUsernameGenerationOptions: (options?: StorageOptions) => Promise<any>;
|
||||||
|
setUsernameGenerationOptions: (value: any, options?: StorageOptions) => Promise<void>;
|
||||||
|
getGeneratorOptions: (options?: StorageOptions) => Promise<any>;
|
||||||
|
setGeneratorOptions: (value: any, options?: StorageOptions) => Promise<void>;
|
||||||
getProtectedPin: (options?: StorageOptions) => Promise<string>;
|
getProtectedPin: (options?: StorageOptions) => Promise<string>;
|
||||||
setProtectedPin: (value: string, options?: StorageOptions) => Promise<void>;
|
setProtectedPin: (value: string, options?: StorageOptions) => Promise<void>;
|
||||||
getProviders: (options?: StorageOptions) => Promise<{ [id: string]: ProviderData }>;
|
getProviders: (options?: StorageOptions) => Promise<{ [id: string]: ProviderData }>;
|
||||||
|
8
common/src/abstractions/usernameGeneration.service.ts
Normal file
8
common/src/abstractions/usernameGeneration.service.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
export abstract class UsernameGenerationService {
|
||||||
|
generateUsername: (options: any) => Promise<string>;
|
||||||
|
generateWord: (options: any) => Promise<string>;
|
||||||
|
generateSubaddress: (options: any) => Promise<string>;
|
||||||
|
generateCatchall: (options: any) => Promise<string>;
|
||||||
|
getOptions: () => Promise<any>;
|
||||||
|
saveOptions: (options: any) => Promise<void>;
|
||||||
|
}
|
@ -125,6 +125,8 @@ export class AccountSettings {
|
|||||||
minimizeOnCopyToClipboard?: boolean;
|
minimizeOnCopyToClipboard?: boolean;
|
||||||
neverDomains?: { [id: string]: any };
|
neverDomains?: { [id: string]: any };
|
||||||
passwordGenerationOptions?: any;
|
passwordGenerationOptions?: any;
|
||||||
|
usernameGenerationOptions?: any;
|
||||||
|
generatorOptions?: any;
|
||||||
pinProtected?: EncryptionPair<string, EncString> = new EncryptionPair<string, EncString>();
|
pinProtected?: EncryptionPair<string, EncString> = new EncryptionPair<string, EncString>();
|
||||||
protectedPin?: string;
|
protectedPin?: string;
|
||||||
settings?: any; // TODO: Merge whatever is going on here into the AccountSettings model properly
|
settings?: any; // TODO: Merge whatever is going on here into the AccountSettings model properly
|
||||||
|
@ -1797,6 +1797,40 @@ export class StateService<
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getUsernameGenerationOptions(options?: StorageOptions): Promise<any> {
|
||||||
|
return (
|
||||||
|
await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions()))
|
||||||
|
)?.settings?.usernameGenerationOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
async setUsernameGenerationOptions(value: any, options?: StorageOptions): Promise<void> {
|
||||||
|
const account = await this.getAccount(
|
||||||
|
this.reconcileOptions(options, await this.defaultOnDiskOptions())
|
||||||
|
);
|
||||||
|
account.settings.usernameGenerationOptions = value;
|
||||||
|
await this.saveAccount(
|
||||||
|
account,
|
||||||
|
this.reconcileOptions(options, await this.defaultOnDiskOptions())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getGeneratorOptions(options?: StorageOptions): Promise<any> {
|
||||||
|
return (
|
||||||
|
await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions()))
|
||||||
|
)?.settings?.generatorOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
async setGeneratorOptions(value: any, options?: StorageOptions): Promise<void> {
|
||||||
|
const account = await this.getAccount(
|
||||||
|
this.reconcileOptions(options, await this.defaultOnDiskOptions())
|
||||||
|
);
|
||||||
|
account.settings.generatorOptions = value;
|
||||||
|
await this.saveAccount(
|
||||||
|
account,
|
||||||
|
this.reconcileOptions(options, await this.defaultOnDiskOptions())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
async getProtectedPin(options?: StorageOptions): Promise<string> {
|
async getProtectedPin(options?: StorageOptions): Promise<string> {
|
||||||
return (
|
return (
|
||||||
await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions()))
|
await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions()))
|
||||||
|
128
common/src/services/usernameGeneration.service.ts
Normal file
128
common/src/services/usernameGeneration.service.ts
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
import { CryptoService } from "../abstractions/crypto.service";
|
||||||
|
import { StateService } from "../abstractions/state.service";
|
||||||
|
import { UsernameGenerationService as BaseUsernameGenerationService } from "../abstractions/usernameGeneration.service";
|
||||||
|
import { EEFLongWordList } from "../misc/wordlist";
|
||||||
|
|
||||||
|
const DefaultOptions = {
|
||||||
|
type: "word",
|
||||||
|
wordCapitalize: true,
|
||||||
|
wordIncludeNumber: true,
|
||||||
|
subaddressType: "random",
|
||||||
|
catchallType: "random",
|
||||||
|
};
|
||||||
|
|
||||||
|
export class UsernameGenerationService implements BaseUsernameGenerationService {
|
||||||
|
constructor(private cryptoService: CryptoService, private stateService: StateService) {}
|
||||||
|
|
||||||
|
generateUsername(options: any): Promise<string> {
|
||||||
|
if (options.type === "catchall") {
|
||||||
|
return this.generateCatchall(options);
|
||||||
|
} else if (options.type === "subaddress") {
|
||||||
|
return this.generateSubaddress(options);
|
||||||
|
} else if (options.type === "forwarded") {
|
||||||
|
return this.generateSubaddress(options);
|
||||||
|
} else {
|
||||||
|
return this.generateWord(options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async generateWord(options: any): Promise<string> {
|
||||||
|
const o = Object.assign({}, DefaultOptions, options);
|
||||||
|
|
||||||
|
if (o.wordCapitalize == null) {
|
||||||
|
o.wordCapitalize = true;
|
||||||
|
}
|
||||||
|
if (o.wordIncludeNumber == null) {
|
||||||
|
o.wordIncludeNumber = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const wordIndex = await this.cryptoService.randomNumber(0, EEFLongWordList.length - 1);
|
||||||
|
let word = EEFLongWordList[wordIndex];
|
||||||
|
if (o.wordCapitalize) {
|
||||||
|
word = word.charAt(0).toUpperCase() + word.slice(1);
|
||||||
|
}
|
||||||
|
if (o.wordIncludeNumber) {
|
||||||
|
const num = await this.cryptoService.randomNumber(1, 9999);
|
||||||
|
word = word + this.zeroPad(num.toString(), 4);
|
||||||
|
}
|
||||||
|
return word;
|
||||||
|
}
|
||||||
|
|
||||||
|
async generateSubaddress(options: any): Promise<string> {
|
||||||
|
const o = Object.assign({}, DefaultOptions, options);
|
||||||
|
|
||||||
|
const subaddressEmail = o.subaddressEmail;
|
||||||
|
if (subaddressEmail == null || subaddressEmail.length < 3) {
|
||||||
|
return o.subaddressEmail;
|
||||||
|
}
|
||||||
|
const atIndex = subaddressEmail.indexOf("@");
|
||||||
|
if (atIndex < 1 || atIndex >= subaddressEmail.length - 1) {
|
||||||
|
return subaddressEmail;
|
||||||
|
}
|
||||||
|
if (o.subaddressType == null) {
|
||||||
|
o.subaddressType = "random";
|
||||||
|
}
|
||||||
|
|
||||||
|
const emailBeginning = subaddressEmail.substr(0, atIndex);
|
||||||
|
const emailEnding = subaddressEmail.substr(atIndex + 1, subaddressEmail.length);
|
||||||
|
|
||||||
|
let subaddressString = "";
|
||||||
|
if (o.subaddressType === "random") {
|
||||||
|
subaddressString = await this.randomString(8);
|
||||||
|
} else if (o.subaddressType === "website-name") {
|
||||||
|
subaddressString = o.website;
|
||||||
|
}
|
||||||
|
return emailBeginning + "+" + subaddressString + "@" + emailEnding;
|
||||||
|
}
|
||||||
|
|
||||||
|
async generateCatchall(options: any): Promise<string> {
|
||||||
|
const o = Object.assign({}, DefaultOptions, options);
|
||||||
|
|
||||||
|
if (o.catchallDomain == null || o.catchallDomain === "") {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (o.catchallType == null) {
|
||||||
|
o.catchallType = "random";
|
||||||
|
}
|
||||||
|
|
||||||
|
let startString = "";
|
||||||
|
if (o.catchallType === "random") {
|
||||||
|
startString = await this.randomString(8);
|
||||||
|
} else if (o.catchallType === "website-name") {
|
||||||
|
startString = o.website;
|
||||||
|
}
|
||||||
|
return startString + "@" + o.catchallDomain;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getOptions(): Promise<any> {
|
||||||
|
let options = await this.stateService.getUsernameGenerationOptions();
|
||||||
|
if (options == null) {
|
||||||
|
options = DefaultOptions;
|
||||||
|
} else {
|
||||||
|
options = Object.assign({}, DefaultOptions, options);
|
||||||
|
}
|
||||||
|
await this.stateService.setUsernameGenerationOptions(options);
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
async saveOptions(options: any) {
|
||||||
|
await this.stateService.setUsernameGenerationOptions(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async randomString(length: number) {
|
||||||
|
let str = "";
|
||||||
|
const charSet = "abcdefghijklmnopqrstuvwxyz1234567890";
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
|
const randomCharIndex = await this.cryptoService.randomNumber(0, charSet.length - 1);
|
||||||
|
str += charSet.charAt(randomCharIndex);
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ref: https://stackoverflow.com/a/10073788
|
||||||
|
private zeroPad(number: string, width: number) {
|
||||||
|
return number.length >= width
|
||||||
|
? number
|
||||||
|
: new Array(width - number.length + 1).join("0") + number;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user