mirror of
https://github.com/bitwarden/browser.git
synced 2025-01-05 18:47:52 +01:00
"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[],
|
getAllDecryptedForUrl: (url: string, includeOtherTypes?: CipherType[],
|
||||||
defaultMatch?: UriMatchType) => Promise<CipherView[]>;
|
defaultMatch?: UriMatchType) => Promise<CipherView[]>;
|
||||||
getAllFromApiForOrganization: (organizationId: string) => Promise<CipherView[]>;
|
getAllFromApiForOrganization: (organizationId: string) => Promise<CipherView[]>;
|
||||||
getLastUsedForUrl: (url: string) => Promise<CipherView>;
|
getLastUsedForUrl: (url: string, autofillOnPageLoad: boolean) => Promise<CipherView>;
|
||||||
getLastLaunchedForUrl: (url: string) => Promise<CipherView>;
|
getLastLaunchedForUrl: (url: string, autofillOnPageLoad: boolean) => Promise<CipherView>;
|
||||||
getNextCipherForUrl: (url: string) => Promise<CipherView>;
|
getNextCipherForUrl: (url: string) => Promise<CipherView>;
|
||||||
updateLastUsedIndexForUrl: (url: string) => void;
|
updateLastUsedIndexForUrl: (url: string) => void;
|
||||||
updateLastUsedDate: (id: string) => Promise<void>;
|
updateLastUsedDate: (id: string) => Promise<void>;
|
||||||
|
@ -84,6 +84,7 @@ export class AddEditComponent implements OnInit {
|
|||||||
addFieldTypeOptions: any[];
|
addFieldTypeOptions: any[];
|
||||||
uriMatchOptions: any[];
|
uriMatchOptions: any[];
|
||||||
ownershipOptions: any[] = [];
|
ownershipOptions: any[] = [];
|
||||||
|
autofillOnPageLoadOptions: any[];
|
||||||
currentDate = new Date();
|
currentDate = new Date();
|
||||||
allowPersonal = true;
|
allowPersonal = true;
|
||||||
reprompt: boolean = false;
|
reprompt: boolean = false;
|
||||||
@ -151,6 +152,11 @@ export class AddEditComponent implements OnInit {
|
|||||||
{ name: i18nService.t('exact'), value: UriMatchType.Exact },
|
{ name: i18nService.t('exact'), value: UriMatchType.Exact },
|
||||||
{ name: i18nService.t('never'), value: UriMatchType.Never },
|
{ 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() {
|
async ngOnInit() {
|
||||||
|
@ -8,6 +8,7 @@ export class LoginApi extends BaseResponse {
|
|||||||
password: string;
|
password: string;
|
||||||
passwordRevisionDate: string;
|
passwordRevisionDate: string;
|
||||||
totp: string;
|
totp: string;
|
||||||
|
autofillOnPageLoad: boolean;
|
||||||
|
|
||||||
constructor(data: any = null) {
|
constructor(data: any = null) {
|
||||||
super(data);
|
super(data);
|
||||||
@ -18,6 +19,7 @@ export class LoginApi extends BaseResponse {
|
|||||||
this.password = this.getResponseProperty('Password');
|
this.password = this.getResponseProperty('Password');
|
||||||
this.passwordRevisionDate = this.getResponseProperty('PasswordRevisionDate');
|
this.passwordRevisionDate = this.getResponseProperty('PasswordRevisionDate');
|
||||||
this.totp = this.getResponseProperty('Totp');
|
this.totp = this.getResponseProperty('Totp');
|
||||||
|
this.autofillOnPageLoad = this.getResponseProperty('AutofillOnPageLoad');
|
||||||
|
|
||||||
const uris = this.getResponseProperty('Uris');
|
const uris = this.getResponseProperty('Uris');
|
||||||
if (uris != null) {
|
if (uris != null) {
|
||||||
|
@ -8,6 +8,7 @@ export class LoginData {
|
|||||||
password: string;
|
password: string;
|
||||||
passwordRevisionDate: string;
|
passwordRevisionDate: string;
|
||||||
totp: string;
|
totp: string;
|
||||||
|
autofillOnPageLoad: boolean;
|
||||||
|
|
||||||
constructor(data?: LoginApi) {
|
constructor(data?: LoginApi) {
|
||||||
if (data == null) {
|
if (data == null) {
|
||||||
@ -18,6 +19,7 @@ export class LoginData {
|
|||||||
this.password = data.password;
|
this.password = data.password;
|
||||||
this.passwordRevisionDate = data.passwordRevisionDate;
|
this.passwordRevisionDate = data.passwordRevisionDate;
|
||||||
this.totp = data.totp;
|
this.totp = data.totp;
|
||||||
|
this.autofillOnPageLoad = data.autofillOnPageLoad;
|
||||||
|
|
||||||
if (data.uris) {
|
if (data.uris) {
|
||||||
this.uris = data.uris.map(u => new LoginUriData(u));
|
this.uris = data.uris.map(u => new LoginUriData(u));
|
||||||
|
@ -14,6 +14,7 @@ export class Login extends Domain {
|
|||||||
password: EncString;
|
password: EncString;
|
||||||
passwordRevisionDate?: Date;
|
passwordRevisionDate?: Date;
|
||||||
totp: EncString;
|
totp: EncString;
|
||||||
|
autofillOnPageLoad: boolean;
|
||||||
|
|
||||||
constructor(obj?: LoginData, alreadyEncrypted: boolean = false) {
|
constructor(obj?: LoginData, alreadyEncrypted: boolean = false) {
|
||||||
super();
|
super();
|
||||||
@ -22,6 +23,7 @@ export class Login extends Domain {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.passwordRevisionDate = obj.passwordRevisionDate != null ? new Date(obj.passwordRevisionDate) : null;
|
this.passwordRevisionDate = obj.passwordRevisionDate != null ? new Date(obj.passwordRevisionDate) : null;
|
||||||
|
this.autofillOnPageLoad = obj.autofillOnPageLoad;
|
||||||
this.buildDomainModel(this, obj, {
|
this.buildDomainModel(this, obj, {
|
||||||
username: null,
|
username: null,
|
||||||
password: null,
|
password: null,
|
||||||
@ -57,6 +59,7 @@ export class Login extends Domain {
|
|||||||
toLoginData(): LoginData {
|
toLoginData(): LoginData {
|
||||||
const l = new LoginData();
|
const l = new LoginData();
|
||||||
l.passwordRevisionDate = this.passwordRevisionDate != null ? this.passwordRevisionDate.toISOString() : null;
|
l.passwordRevisionDate = this.passwordRevisionDate != null ? this.passwordRevisionDate.toISOString() : null;
|
||||||
|
l.autofillOnPageLoad = this.autofillOnPageLoad;
|
||||||
this.buildDataModel(this, l, {
|
this.buildDataModel(this, l, {
|
||||||
username: null,
|
username: null,
|
||||||
password: null,
|
password: null,
|
||||||
|
@ -51,6 +51,7 @@ export class CipherRequest {
|
|||||||
this.login.passwordRevisionDate = cipher.login.passwordRevisionDate != null ?
|
this.login.passwordRevisionDate = cipher.login.passwordRevisionDate != null ?
|
||||||
cipher.login.passwordRevisionDate.toISOString() : null;
|
cipher.login.passwordRevisionDate.toISOString() : null;
|
||||||
this.login.totp = cipher.login.totp ? cipher.login.totp.encryptedString : null;
|
this.login.totp = cipher.login.totp ? cipher.login.totp.encryptedString : null;
|
||||||
|
this.login.autofillOnPageLoad = cipher.login.autofillOnPageLoad;
|
||||||
|
|
||||||
if (cipher.login.uris != null) {
|
if (cipher.login.uris != null) {
|
||||||
this.login.uris = cipher.login.uris.map(u => {
|
this.login.uris = cipher.login.uris.map(u => {
|
||||||
|
@ -10,6 +10,7 @@ export class LoginView implements View {
|
|||||||
passwordRevisionDate?: Date = null;
|
passwordRevisionDate?: Date = null;
|
||||||
totp: string = null;
|
totp: string = null;
|
||||||
uris: LoginUriView[] = null;
|
uris: LoginUriView[] = null;
|
||||||
|
autofillOnPageLoad: boolean = null;
|
||||||
|
|
||||||
constructor(l?: Login) {
|
constructor(l?: Login) {
|
||||||
if (!l) {
|
if (!l) {
|
||||||
@ -17,6 +18,7 @@ export class LoginView implements View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.passwordRevisionDate = l.passwordRevisionDate;
|
this.passwordRevisionDate = l.passwordRevisionDate;
|
||||||
|
this.autofillOnPageLoad = l.autofillOnPageLoad;
|
||||||
}
|
}
|
||||||
|
|
||||||
get uri(): string {
|
get uri(): string {
|
||||||
|
@ -454,16 +454,16 @@ export class CipherService implements CipherServiceAbstraction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async getLastUsedForUrl(url: string): Promise<CipherView> {
|
async getLastUsedForUrl(url: string, autofillOnPageLoad: boolean = false): Promise<CipherView> {
|
||||||
return this.getCipherForUrl(url, true, false);
|
return this.getCipherForUrl(url, true, false, autofillOnPageLoad);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getLastLaunchedForUrl(url: string): Promise<CipherView> {
|
async getLastLaunchedForUrl(url: string, autofillOnPageLoad: boolean = false): Promise<CipherView> {
|
||||||
return this.getCipherForUrl(url, false, true);
|
return this.getCipherForUrl(url, false, true, autofillOnPageLoad);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getNextCipherForUrl(url: string): Promise<CipherView> {
|
async getNextCipherForUrl(url: string): Promise<CipherView> {
|
||||||
return this.getCipherForUrl(url, false, false);
|
return this.getCipherForUrl(url, false, false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
updateLastUsedIndexForUrl(url: string) {
|
updateLastUsedIndexForUrl(url: string) {
|
||||||
@ -1032,6 +1032,7 @@ export class CipherService implements CipherServiceAbstraction {
|
|||||||
case CipherType.Login:
|
case CipherType.Login:
|
||||||
cipher.login = new Login();
|
cipher.login = new Login();
|
||||||
cipher.login.passwordRevisionDate = model.login.passwordRevisionDate;
|
cipher.login.passwordRevisionDate = model.login.passwordRevisionDate;
|
||||||
|
cipher.login.autofillOnPageLoad = model.login.autofillOnPageLoad;
|
||||||
await this.encryptObjProperty(model.login, cipher.login, {
|
await this.encryptObjProperty(model.login, cipher.login, {
|
||||||
username: null,
|
username: null,
|
||||||
password: null,
|
password: null,
|
||||||
@ -1093,21 +1094,33 @@ export class CipherService implements CipherServiceAbstraction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getCipherForUrl(url: string, lastUsed: boolean, lastLaunched: boolean): Promise<CipherView> {
|
private async getCipherForUrl(url: string, lastUsed: boolean, lastLaunched: boolean, autofillOnPageLoad: boolean): Promise<CipherView> {
|
||||||
if (!this.sortedCiphersCache.isCached(url)) {
|
const cacheKey = autofillOnPageLoad ? 'autofillOnPageLoad-' + url : url;
|
||||||
const ciphers = await this.getAllDecryptedForUrl(url);
|
|
||||||
|
if (!this.sortedCiphersCache.isCached(cacheKey)) {
|
||||||
|
let ciphers = await this.getAllDecryptedForUrl(url);
|
||||||
if (!ciphers) {
|
if (!ciphers) {
|
||||||
return null;
|
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) {
|
if (lastLaunched) {
|
||||||
return this.sortedCiphersCache.getLastLaunched(url);
|
return this.sortedCiphersCache.getLastLaunched(cacheKey);
|
||||||
} else if (lastUsed) {
|
} else if (lastUsed) {
|
||||||
return this.sortedCiphersCache.getLastUsed(url);
|
return this.sortedCiphersCache.getLastUsed(cacheKey);
|
||||||
} else {
|
} 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 disableBadgeCounterKey: string = 'disableBadgeCounter';
|
||||||
static readonly disableAutoTotpCopyKey: string = 'disableAutoTotpCopy';
|
static readonly disableAutoTotpCopyKey: string = 'disableAutoTotpCopy';
|
||||||
static readonly enableAutoFillOnPageLoadKey: string = 'enableAutoFillOnPageLoad';
|
static readonly enableAutoFillOnPageLoadKey: string = 'enableAutoFillOnPageLoad';
|
||||||
|
static readonly autoFillOnPageLoadDefaultKey: string = 'autoFillOnPageLoadDefault';
|
||||||
static readonly vaultTimeoutKey: string = 'lockOption';
|
static readonly vaultTimeoutKey: string = 'lockOption';
|
||||||
static readonly vaultTimeoutActionKey: string = 'vaultTimeoutAction';
|
static readonly vaultTimeoutActionKey: string = 'vaultTimeoutAction';
|
||||||
static readonly lastActiveKey: string = 'lastActive';
|
static readonly lastActiveKey: string = 'lastActive';
|
||||||
@ -39,6 +40,7 @@ export class ConstantsService {
|
|||||||
readonly disableBadgeCounterKey: string = ConstantsService.disableBadgeCounterKey;
|
readonly disableBadgeCounterKey: string = ConstantsService.disableBadgeCounterKey;
|
||||||
readonly disableAutoTotpCopyKey: string = ConstantsService.disableAutoTotpCopyKey;
|
readonly disableAutoTotpCopyKey: string = ConstantsService.disableAutoTotpCopyKey;
|
||||||
readonly enableAutoFillOnPageLoadKey: string = ConstantsService.enableAutoFillOnPageLoadKey;
|
readonly enableAutoFillOnPageLoadKey: string = ConstantsService.enableAutoFillOnPageLoadKey;
|
||||||
|
readonly autoFillOnPageLoadDefaultKey: string = ConstantsService.autoFillOnPageLoadDefaultKey;
|
||||||
readonly vaultTimeoutKey: string = ConstantsService.vaultTimeoutKey;
|
readonly vaultTimeoutKey: string = ConstantsService.vaultTimeoutKey;
|
||||||
readonly vaultTimeoutActionKey: string = ConstantsService.vaultTimeoutActionKey;
|
readonly vaultTimeoutActionKey: string = ConstantsService.vaultTimeoutActionKey;
|
||||||
readonly lastActiveKey: string = ConstantsService.lastActiveKey;
|
readonly lastActiveKey: string = ConstantsService.lastActiveKey;
|
||||||
|
Loading…
Reference in New Issue
Block a user