"Auto-fill on page load" options (#199)
* add autofill on page load props to models and view For new per-login autofill on page load settings * filter and cache ciphers per autofill setting Used by the new autofill on page load feature to identify matching ciphers and filter according to their autofill setting * fix null check on array * fix linting and style errors * change cacheKey to avoid collision with real url * Fix linting, set default value for aopl-options * Fix linting * update UI * Remove autofillOnPageLoad from export * Change enum to boolean * Add storage key for autofillOnPageLoad default * fix style
This commit is contained in:
parent
7b3f9f12a4
commit
3d4ecaeb6a
|
@ -24,8 +24,8 @@ export abstract class CipherService {
|
|||
getAllDecryptedForUrl: (url: string, includeOtherTypes?: CipherType[],
|
||||
defaultMatch?: UriMatchType) => Promise<CipherView[]>;
|
||||
getAllFromApiForOrganization: (organizationId: string) => Promise<CipherView[]>;
|
||||
getLastUsedForUrl: (url: string) => Promise<CipherView>;
|
||||
getLastLaunchedForUrl: (url: string) => Promise<CipherView>;
|
||||
getLastUsedForUrl: (url: string, autofillOnPageLoad: boolean) => Promise<CipherView>;
|
||||
getLastLaunchedForUrl: (url: string, autofillOnPageLoad: boolean) => Promise<CipherView>;
|
||||
getNextCipherForUrl: (url: string) => Promise<CipherView>;
|
||||
updateLastUsedIndexForUrl: (url: string) => void;
|
||||
updateLastUsedDate: (id: string) => Promise<void>;
|
||||
|
|
|
@ -84,6 +84,7 @@ export class AddEditComponent implements OnInit {
|
|||
addFieldTypeOptions: any[];
|
||||
uriMatchOptions: any[];
|
||||
ownershipOptions: any[] = [];
|
||||
autofillOnPageLoadOptions: any[];
|
||||
currentDate = new Date();
|
||||
allowPersonal = true;
|
||||
reprompt: boolean = false;
|
||||
|
@ -151,6 +152,11 @@ export class AddEditComponent implements OnInit {
|
|||
{ name: i18nService.t('exact'), value: UriMatchType.Exact },
|
||||
{ name: i18nService.t('never'), value: UriMatchType.Never },
|
||||
];
|
||||
this.autofillOnPageLoadOptions = [
|
||||
{ name: i18nService.t('autoFillOnPageLoadUseDefault'), value: null },
|
||||
{ name: i18nService.t('autoFillOnPageLoadYes'), value: true },
|
||||
{ name: i18nService.t('autoFillOnPageLoadNo'), value: false },
|
||||
];
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
|
|
|
@ -8,6 +8,7 @@ export class LoginApi extends BaseResponse {
|
|||
password: string;
|
||||
passwordRevisionDate: string;
|
||||
totp: string;
|
||||
autofillOnPageLoad: boolean;
|
||||
|
||||
constructor(data: any = null) {
|
||||
super(data);
|
||||
|
@ -18,6 +19,7 @@ export class LoginApi extends BaseResponse {
|
|||
this.password = this.getResponseProperty('Password');
|
||||
this.passwordRevisionDate = this.getResponseProperty('PasswordRevisionDate');
|
||||
this.totp = this.getResponseProperty('Totp');
|
||||
this.autofillOnPageLoad = this.getResponseProperty('AutofillOnPageLoad');
|
||||
|
||||
const uris = this.getResponseProperty('Uris');
|
||||
if (uris != null) {
|
||||
|
|
|
@ -8,6 +8,7 @@ export class LoginData {
|
|||
password: string;
|
||||
passwordRevisionDate: string;
|
||||
totp: string;
|
||||
autofillOnPageLoad: boolean;
|
||||
|
||||
constructor(data?: LoginApi) {
|
||||
if (data == null) {
|
||||
|
@ -18,6 +19,7 @@ export class LoginData {
|
|||
this.password = data.password;
|
||||
this.passwordRevisionDate = data.passwordRevisionDate;
|
||||
this.totp = data.totp;
|
||||
this.autofillOnPageLoad = data.autofillOnPageLoad;
|
||||
|
||||
if (data.uris) {
|
||||
this.uris = data.uris.map(u => new LoginUriData(u));
|
||||
|
|
|
@ -14,6 +14,7 @@ export class Login extends Domain {
|
|||
password: EncString;
|
||||
passwordRevisionDate?: Date;
|
||||
totp: EncString;
|
||||
autofillOnPageLoad: boolean;
|
||||
|
||||
constructor(obj?: LoginData, alreadyEncrypted: boolean = false) {
|
||||
super();
|
||||
|
@ -22,6 +23,7 @@ export class Login extends Domain {
|
|||
}
|
||||
|
||||
this.passwordRevisionDate = obj.passwordRevisionDate != null ? new Date(obj.passwordRevisionDate) : null;
|
||||
this.autofillOnPageLoad = obj.autofillOnPageLoad;
|
||||
this.buildDomainModel(this, obj, {
|
||||
username: null,
|
||||
password: null,
|
||||
|
@ -57,6 +59,7 @@ export class Login extends Domain {
|
|||
toLoginData(): LoginData {
|
||||
const l = new LoginData();
|
||||
l.passwordRevisionDate = this.passwordRevisionDate != null ? this.passwordRevisionDate.toISOString() : null;
|
||||
l.autofillOnPageLoad = this.autofillOnPageLoad;
|
||||
this.buildDataModel(this, l, {
|
||||
username: null,
|
||||
password: null,
|
||||
|
|
|
@ -51,6 +51,7 @@ export class CipherRequest {
|
|||
this.login.passwordRevisionDate = cipher.login.passwordRevisionDate != null ?
|
||||
cipher.login.passwordRevisionDate.toISOString() : null;
|
||||
this.login.totp = cipher.login.totp ? cipher.login.totp.encryptedString : null;
|
||||
this.login.autofillOnPageLoad = cipher.login.autofillOnPageLoad;
|
||||
|
||||
if (cipher.login.uris != null) {
|
||||
this.login.uris = cipher.login.uris.map(u => {
|
||||
|
|
|
@ -10,6 +10,7 @@ export class LoginView implements View {
|
|||
passwordRevisionDate?: Date = null;
|
||||
totp: string = null;
|
||||
uris: LoginUriView[] = null;
|
||||
autofillOnPageLoad: boolean = null;
|
||||
|
||||
constructor(l?: Login) {
|
||||
if (!l) {
|
||||
|
@ -17,6 +18,7 @@ export class LoginView implements View {
|
|||
}
|
||||
|
||||
this.passwordRevisionDate = l.passwordRevisionDate;
|
||||
this.autofillOnPageLoad = l.autofillOnPageLoad;
|
||||
}
|
||||
|
||||
get uri(): string {
|
||||
|
|
|
@ -454,16 +454,16 @@ export class CipherService implements CipherServiceAbstraction {
|
|||
}
|
||||
}
|
||||
|
||||
async getLastUsedForUrl(url: string): Promise<CipherView> {
|
||||
return this.getCipherForUrl(url, true, false);
|
||||
async getLastUsedForUrl(url: string, autofillOnPageLoad: boolean = false): Promise<CipherView> {
|
||||
return this.getCipherForUrl(url, true, false, autofillOnPageLoad);
|
||||
}
|
||||
|
||||
async getLastLaunchedForUrl(url: string): Promise<CipherView> {
|
||||
return this.getCipherForUrl(url, false, true);
|
||||
async getLastLaunchedForUrl(url: string, autofillOnPageLoad: boolean = false): Promise<CipherView> {
|
||||
return this.getCipherForUrl(url, false, true, autofillOnPageLoad);
|
||||
}
|
||||
|
||||
async getNextCipherForUrl(url: string): Promise<CipherView> {
|
||||
return this.getCipherForUrl(url, false, false);
|
||||
return this.getCipherForUrl(url, false, false, false);
|
||||
}
|
||||
|
||||
updateLastUsedIndexForUrl(url: string) {
|
||||
|
@ -1032,6 +1032,7 @@ export class CipherService implements CipherServiceAbstraction {
|
|||
case CipherType.Login:
|
||||
cipher.login = new Login();
|
||||
cipher.login.passwordRevisionDate = model.login.passwordRevisionDate;
|
||||
cipher.login.autofillOnPageLoad = model.login.autofillOnPageLoad;
|
||||
await this.encryptObjProperty(model.login, cipher.login, {
|
||||
username: null,
|
||||
password: null,
|
||||
|
@ -1093,21 +1094,33 @@ export class CipherService implements CipherServiceAbstraction {
|
|||
}
|
||||
}
|
||||
|
||||
private async getCipherForUrl(url: string, lastUsed: boolean, lastLaunched: boolean): Promise<CipherView> {
|
||||
if (!this.sortedCiphersCache.isCached(url)) {
|
||||
const ciphers = await this.getAllDecryptedForUrl(url);
|
||||
private async getCipherForUrl(url: string, lastUsed: boolean, lastLaunched: boolean, autofillOnPageLoad: boolean): Promise<CipherView> {
|
||||
const cacheKey = autofillOnPageLoad ? 'autofillOnPageLoad-' + url : url;
|
||||
|
||||
if (!this.sortedCiphersCache.isCached(cacheKey)) {
|
||||
let ciphers = await this.getAllDecryptedForUrl(url);
|
||||
if (!ciphers) {
|
||||
return null;
|
||||
}
|
||||
this.sortedCiphersCache.addCiphers(url, ciphers);
|
||||
|
||||
if (autofillOnPageLoad) {
|
||||
const autofillOnPageLoadDefault = await this.storageService.get(ConstantsService.autoFillOnPageLoadDefaultKey);
|
||||
ciphers = ciphers.filter(cipher => cipher.login.autofillOnPageLoad ||
|
||||
(cipher.login.autofillOnPageLoad === null && autofillOnPageLoadDefault));
|
||||
if (ciphers.length === 0) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
this.sortedCiphersCache.addCiphers(cacheKey, ciphers);
|
||||
}
|
||||
|
||||
if (lastLaunched) {
|
||||
return this.sortedCiphersCache.getLastLaunched(url);
|
||||
return this.sortedCiphersCache.getLastLaunched(cacheKey);
|
||||
} else if (lastUsed) {
|
||||
return this.sortedCiphersCache.getLastUsed(url);
|
||||
return this.sortedCiphersCache.getLastUsed(cacheKey);
|
||||
} else {
|
||||
return this.sortedCiphersCache.getNext(url);
|
||||
return this.sortedCiphersCache.getNext(cacheKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ export class ConstantsService {
|
|||
static readonly disableBadgeCounterKey: string = 'disableBadgeCounter';
|
||||
static readonly disableAutoTotpCopyKey: string = 'disableAutoTotpCopy';
|
||||
static readonly enableAutoFillOnPageLoadKey: string = 'enableAutoFillOnPageLoad';
|
||||
static readonly autoFillOnPageLoadDefaultKey: string = 'autoFillOnPageLoadDefault';
|
||||
static readonly vaultTimeoutKey: string = 'lockOption';
|
||||
static readonly vaultTimeoutActionKey: string = 'vaultTimeoutAction';
|
||||
static readonly lastActiveKey: string = 'lastActive';
|
||||
|
@ -39,6 +40,7 @@ export class ConstantsService {
|
|||
readonly disableBadgeCounterKey: string = ConstantsService.disableBadgeCounterKey;
|
||||
readonly disableAutoTotpCopyKey: string = ConstantsService.disableAutoTotpCopyKey;
|
||||
readonly enableAutoFillOnPageLoadKey: string = ConstantsService.enableAutoFillOnPageLoadKey;
|
||||
readonly autoFillOnPageLoadDefaultKey: string = ConstantsService.autoFillOnPageLoadDefaultKey;
|
||||
readonly vaultTimeoutKey: string = ConstantsService.vaultTimeoutKey;
|
||||
readonly vaultTimeoutActionKey: string = ConstantsService.vaultTimeoutActionKey;
|
||||
readonly lastActiveKey: string = ConstantsService.lastActiveKey;
|
||||
|
|
Loading…
Reference in New Issue