[AC-1046] activate autofill on page load policy (#4860)
* [EC-1046] add activate autofill policy to web * [EC-1046] add local setting if policy needs to be set * [AC-1046] activate autofill on page load if flag exists * [AC-1046] move activation to current tab page * [AC-1046] add warning to autofill policy * [AC-1046] add useActivateAutofillPolicy to organization reponse * [AC-1046] autofill to auto-fill
This commit is contained in:
parent
e8faf12ac1
commit
7892834f97
|
@ -2120,6 +2120,9 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"autofillPageLoadPolicyActivated": {
|
||||||
|
"message": "Your organization policies have turned on auto-fill on page load."
|
||||||
|
},
|
||||||
"howToAutofill": {
|
"howToAutofill": {
|
||||||
"message": "How to auto-fill"
|
"message": "How to auto-fill"
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
import { BehaviorSubject } from "rxjs";
|
import { BehaviorSubject, filter, map, Observable, switchMap, tap } from "rxjs";
|
||||||
import { Jsonify } from "type-fest";
|
import { Jsonify } from "type-fest";
|
||||||
|
|
||||||
|
import { OrganizationService } from "@bitwarden/common/abstractions/organization/organization.service.abstraction";
|
||||||
|
import { StateService } from "@bitwarden/common/abstractions/state.service";
|
||||||
|
import { PolicyType } from "@bitwarden/common/enums/policyType";
|
||||||
import { Policy } from "@bitwarden/common/models/domain/policy";
|
import { Policy } from "@bitwarden/common/models/domain/policy";
|
||||||
import { PolicyService } from "@bitwarden/common/services/policy/policy.service";
|
import { PolicyService } from "@bitwarden/common/services/policy/policy.service";
|
||||||
|
|
||||||
|
@ -13,4 +16,29 @@ export class BrowserPolicyService extends PolicyService {
|
||||||
initializeAs: "array",
|
initializeAs: "array",
|
||||||
})
|
})
|
||||||
protected _policies: BehaviorSubject<Policy[]>;
|
protected _policies: BehaviorSubject<Policy[]>;
|
||||||
|
|
||||||
|
constructor(stateService: StateService, organizationService: OrganizationService) {
|
||||||
|
super(stateService, organizationService);
|
||||||
|
this._policies.pipe(this.handleActivateAutofillPolicy.bind(this)).subscribe();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the ActivateAutofill policy is enabled, save a flag indicating if we need to
|
||||||
|
* enable Autofill on page load.
|
||||||
|
*/
|
||||||
|
private handleActivateAutofillPolicy(policies$: Observable<Policy[]>) {
|
||||||
|
return policies$.pipe(
|
||||||
|
map((policies) => policies.find((p) => p.type == PolicyType.ActivateAutofill && p.enabled)),
|
||||||
|
filter((p) => p != null),
|
||||||
|
switchMap(async (_) => [
|
||||||
|
await this.stateService.getActivateAutoFillOnPageLoadFromPolicy(),
|
||||||
|
await this.stateService.getEnableAutoFillOnPageLoad(),
|
||||||
|
]),
|
||||||
|
tap(([activated, autofillEnabled]) => {
|
||||||
|
if (activated === undefined) {
|
||||||
|
this.stateService.setActivateAutoFillOnPageLoadFromPolicy(!autofillEnabled);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -116,6 +116,17 @@ export class CurrentTabComponent implements OnInit, OnDestroy {
|
||||||
this.search$
|
this.search$
|
||||||
.pipe(debounceTime(500), takeUntil(this.destroy$))
|
.pipe(debounceTime(500), takeUntil(this.destroy$))
|
||||||
.subscribe(() => this.searchVault());
|
.subscribe(() => this.searchVault());
|
||||||
|
|
||||||
|
// activate autofill on page load if policy is set
|
||||||
|
if (await this.stateService.getActivateAutoFillOnPageLoadFromPolicy()) {
|
||||||
|
await this.stateService.setEnableAutoFillOnPageLoad(true);
|
||||||
|
await this.stateService.setActivateAutoFillOnPageLoadFromPolicy(false);
|
||||||
|
this.platformUtilsService.showToast(
|
||||||
|
"info",
|
||||||
|
null,
|
||||||
|
this.i18nService.t("autofillPageLoadPolicyActivated")
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy() {
|
ngOnDestroy() {
|
||||||
|
|
|
@ -6,6 +6,7 @@ import { first } from "rxjs/operators";
|
||||||
|
|
||||||
import { VaultFilter } from "@bitwarden/angular/vault/vault-filter/models/vault-filter.model";
|
import { VaultFilter } from "@bitwarden/angular/vault/vault-filter/models/vault-filter.model";
|
||||||
import { BroadcasterService } from "@bitwarden/common/abstractions/broadcaster.service";
|
import { BroadcasterService } from "@bitwarden/common/abstractions/broadcaster.service";
|
||||||
|
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
||||||
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
|
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
|
||||||
import { SearchService } from "@bitwarden/common/abstractions/search.service";
|
import { SearchService } from "@bitwarden/common/abstractions/search.service";
|
||||||
import { TreeNode } from "@bitwarden/common/models/domain/tree-node";
|
import { TreeNode } from "@bitwarden/common/models/domain/tree-node";
|
||||||
|
@ -72,6 +73,7 @@ export class VaultFilterComponent implements OnInit, OnDestroy {
|
||||||
private allCiphers: CipherView[] = null;
|
private allCiphers: CipherView[] = null;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
private i18nService: I18nService,
|
||||||
private cipherService: CipherService,
|
private cipherService: CipherService,
|
||||||
private router: Router,
|
private router: Router,
|
||||||
private ngZone: NgZone,
|
private ngZone: NgZone,
|
||||||
|
|
|
@ -4853,6 +4853,18 @@
|
||||||
"personalVaultExportPolicyInEffect": {
|
"personalVaultExportPolicyInEffect": {
|
||||||
"message": "One or more organization policies prevents you from exporting your individual vault."
|
"message": "One or more organization policies prevents you from exporting your individual vault."
|
||||||
},
|
},
|
||||||
|
"activateAutofill": {
|
||||||
|
"message": "Activate auto-fill"
|
||||||
|
},
|
||||||
|
"activateAutofillDesc": {
|
||||||
|
"message": "Activate the auto-fill with page load settings on the browser extension for all existing and new members."
|
||||||
|
},
|
||||||
|
"experimentalFeature": {
|
||||||
|
"message": "Compromised or untrusted websites can exploit auto-fill on page load."
|
||||||
|
},
|
||||||
|
"learnMoreAboutAutofill": {
|
||||||
|
"message": "Learn more about auto-fill"
|
||||||
|
},
|
||||||
"selectType": {
|
"selectType": {
|
||||||
"message": "Select SSO type"
|
"message": "Select SSO type"
|
||||||
},
|
},
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { Component } from "@angular/core";
|
||||||
|
|
||||||
import { AppComponent as BaseAppComponent } from "@bitwarden/web-vault/app/app.component";
|
import { AppComponent as BaseAppComponent } from "@bitwarden/web-vault/app/app.component";
|
||||||
|
|
||||||
|
import { ActivateAutofillPolicy } from "./policies/activate-autofill.component";
|
||||||
import { DisablePersonalVaultExportPolicy } from "./policies/disable-personal-vault-export.component";
|
import { DisablePersonalVaultExportPolicy } from "./policies/disable-personal-vault-export.component";
|
||||||
import { MaximumVaultTimeoutPolicy } from "./policies/maximum-vault-timeout.component";
|
import { MaximumVaultTimeoutPolicy } from "./policies/maximum-vault-timeout.component";
|
||||||
|
|
||||||
|
@ -16,6 +17,7 @@ export class AppComponent extends BaseAppComponent {
|
||||||
this.policyListService.addPolicies([
|
this.policyListService.addPolicies([
|
||||||
new MaximumVaultTimeoutPolicy(),
|
new MaximumVaultTimeoutPolicy(),
|
||||||
new DisablePersonalVaultExportPolicy(),
|
new DisablePersonalVaultExportPolicy(),
|
||||||
|
new ActivateAutofillPolicy(),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@ import { WildcardRoutingModule } from "@bitwarden/web-vault/app/wildcard-routing
|
||||||
import { AppRoutingModule } from "./app-routing.module";
|
import { AppRoutingModule } from "./app-routing.module";
|
||||||
import { AppComponent } from "./app.component";
|
import { AppComponent } from "./app.component";
|
||||||
import { OrganizationsModule } from "./organizations/organizations.module";
|
import { OrganizationsModule } from "./organizations/organizations.module";
|
||||||
|
import { ActivateAutofillPolicyComponent } from "./policies/activate-autofill.component";
|
||||||
import { DisablePersonalVaultExportPolicyComponent } from "./policies/disable-personal-vault-export.component";
|
import { DisablePersonalVaultExportPolicyComponent } from "./policies/disable-personal-vault-export.component";
|
||||||
import { MaximumVaultTimeoutPolicyComponent } from "./policies/maximum-vault-timeout.component";
|
import { MaximumVaultTimeoutPolicyComponent } from "./policies/maximum-vault-timeout.component";
|
||||||
|
|
||||||
|
@ -39,6 +40,7 @@ import { MaximumVaultTimeoutPolicyComponent } from "./policies/maximum-vault-tim
|
||||||
AppComponent,
|
AppComponent,
|
||||||
DisablePersonalVaultExportPolicyComponent,
|
DisablePersonalVaultExportPolicyComponent,
|
||||||
MaximumVaultTimeoutPolicyComponent,
|
MaximumVaultTimeoutPolicyComponent,
|
||||||
|
ActivateAutofillPolicyComponent,
|
||||||
],
|
],
|
||||||
bootstrap: [AppComponent],
|
bootstrap: [AppComponent],
|
||||||
})
|
})
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
<app-callout type="warning">
|
||||||
|
{{ "experimentalFeature" | i18n }}
|
||||||
|
<a href="https://bitwarden.com/help/auto-fill-browser/" target="_blank" rel="noopener">{{
|
||||||
|
"learnMoreAboutAutofill" | i18n
|
||||||
|
}}</a>
|
||||||
|
</app-callout>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="form-check">
|
||||||
|
<input
|
||||||
|
class="form-check-input"
|
||||||
|
type="checkbox"
|
||||||
|
id="enabled"
|
||||||
|
[formControl]="enabled"
|
||||||
|
name="Enabled"
|
||||||
|
/>
|
||||||
|
<label class="form-check-label" for="enabled">{{ "turnOn" | i18n }}</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -0,0 +1,25 @@
|
||||||
|
import { Component } from "@angular/core";
|
||||||
|
|
||||||
|
import { PolicyType } from "@bitwarden/common/enums/policyType";
|
||||||
|
import { Organization } from "@bitwarden/common/models/domain/organization";
|
||||||
|
import {
|
||||||
|
BasePolicy,
|
||||||
|
BasePolicyComponent,
|
||||||
|
} from "@bitwarden/web-vault/app/organizations/policies/base-policy.component";
|
||||||
|
|
||||||
|
export class ActivateAutofillPolicy extends BasePolicy {
|
||||||
|
name = "activateAutofill";
|
||||||
|
description = "activateAutofillDesc";
|
||||||
|
type = PolicyType.ActivateAutofill;
|
||||||
|
component = ActivateAutofillPolicyComponent;
|
||||||
|
|
||||||
|
display(organization: Organization) {
|
||||||
|
return organization.useActivateAutofillPolicy;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: "policy-activate-autofill",
|
||||||
|
templateUrl: "activate-autofill.component.html",
|
||||||
|
})
|
||||||
|
export class ActivateAutofillPolicyComponent extends BasePolicyComponent {}
|
|
@ -358,7 +358,13 @@ export abstract class StateService<T extends Account = Account> {
|
||||||
|
|
||||||
getAvatarColor: (options?: StorageOptions) => Promise<string | null | undefined>;
|
getAvatarColor: (options?: StorageOptions) => Promise<string | null | undefined>;
|
||||||
setAvatarColor: (value: string, options?: StorageOptions) => Promise<void>;
|
setAvatarColor: (value: string, options?: StorageOptions) => Promise<void>;
|
||||||
|
getActivateAutoFillOnPageLoadFromPolicy: (
|
||||||
|
options?: StorageOptions
|
||||||
|
) => Promise<boolean | undefined>;
|
||||||
|
setActivateAutoFillOnPageLoadFromPolicy: (
|
||||||
|
value: boolean,
|
||||||
|
options?: StorageOptions
|
||||||
|
) => Promise<void>;
|
||||||
getSMOnboardingTasks: (
|
getSMOnboardingTasks: (
|
||||||
options?: StorageOptions
|
options?: StorageOptions
|
||||||
) => Promise<Record<string, Record<string, boolean>>>;
|
) => Promise<Record<string, Record<string, boolean>>>;
|
||||||
|
|
|
@ -10,4 +10,5 @@ export enum PolicyType {
|
||||||
ResetPassword = 8, // Allows orgs to use reset password : also can enable auto-enrollment during invite flow
|
ResetPassword = 8, // Allows orgs to use reset password : also can enable auto-enrollment during invite flow
|
||||||
MaximumVaultTimeout = 9, // Sets the maximum allowed vault timeout
|
MaximumVaultTimeout = 9, // Sets the maximum allowed vault timeout
|
||||||
DisablePersonalVaultExport = 10, // Disable personal vault export
|
DisablePersonalVaultExport = 10, // Disable personal vault export
|
||||||
|
ActivateAutofill = 11, // Activates autofill with page load on the browser extension
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@ export class OrganizationData {
|
||||||
useCustomPermissions: boolean;
|
useCustomPermissions: boolean;
|
||||||
useResetPassword: boolean;
|
useResetPassword: boolean;
|
||||||
useSecretsManager: boolean;
|
useSecretsManager: boolean;
|
||||||
|
useActivateAutofillPolicy: boolean;
|
||||||
selfHost: boolean;
|
selfHost: boolean;
|
||||||
usersGetPremium: boolean;
|
usersGetPremium: boolean;
|
||||||
seats: number;
|
seats: number;
|
||||||
|
@ -66,6 +67,7 @@ export class OrganizationData {
|
||||||
this.useCustomPermissions = response.useCustomPermissions;
|
this.useCustomPermissions = response.useCustomPermissions;
|
||||||
this.useResetPassword = response.useResetPassword;
|
this.useResetPassword = response.useResetPassword;
|
||||||
this.useSecretsManager = response.useSecretsManager;
|
this.useSecretsManager = response.useSecretsManager;
|
||||||
|
this.useActivateAutofillPolicy = response.useActivateAutofillPolicy;
|
||||||
this.selfHost = response.selfHost;
|
this.selfHost = response.selfHost;
|
||||||
this.usersGetPremium = response.usersGetPremium;
|
this.usersGetPremium = response.usersGetPremium;
|
||||||
this.seats = response.seats;
|
this.seats = response.seats;
|
||||||
|
|
|
@ -238,6 +238,7 @@ export class AccountSettings {
|
||||||
serverConfig?: ServerConfigData;
|
serverConfig?: ServerConfigData;
|
||||||
approveLoginRequests?: boolean;
|
approveLoginRequests?: boolean;
|
||||||
avatarColor?: string;
|
avatarColor?: string;
|
||||||
|
activateAutoFillOnPageLoadFromPolicy?: boolean;
|
||||||
smOnboardingTasks?: Record<string, Record<string, boolean>>;
|
smOnboardingTasks?: Record<string, Record<string, boolean>>;
|
||||||
|
|
||||||
static fromJSON(obj: Jsonify<AccountSettings>): AccountSettings {
|
static fromJSON(obj: Jsonify<AccountSettings>): AccountSettings {
|
||||||
|
|
|
@ -25,6 +25,7 @@ export class Organization {
|
||||||
useCustomPermissions: boolean;
|
useCustomPermissions: boolean;
|
||||||
useResetPassword: boolean;
|
useResetPassword: boolean;
|
||||||
useSecretsManager: boolean;
|
useSecretsManager: boolean;
|
||||||
|
useActivateAutofillPolicy: boolean;
|
||||||
selfHost: boolean;
|
selfHost: boolean;
|
||||||
usersGetPremium: boolean;
|
usersGetPremium: boolean;
|
||||||
seats: number;
|
seats: number;
|
||||||
|
@ -72,6 +73,7 @@ export class Organization {
|
||||||
this.useCustomPermissions = obj.useCustomPermissions;
|
this.useCustomPermissions = obj.useCustomPermissions;
|
||||||
this.useResetPassword = obj.useResetPassword;
|
this.useResetPassword = obj.useResetPassword;
|
||||||
this.useSecretsManager = obj.useSecretsManager;
|
this.useSecretsManager = obj.useSecretsManager;
|
||||||
|
this.useActivateAutofillPolicy = obj.useActivateAutofillPolicy;
|
||||||
this.selfHost = obj.selfHost;
|
this.selfHost = obj.selfHost;
|
||||||
this.usersGetPremium = obj.usersGetPremium;
|
this.usersGetPremium = obj.usersGetPremium;
|
||||||
this.seats = obj.seats;
|
this.seats = obj.seats;
|
||||||
|
|
|
@ -21,6 +21,7 @@ export class ProfileOrganizationResponse extends BaseResponse {
|
||||||
useCustomPermissions: boolean;
|
useCustomPermissions: boolean;
|
||||||
useResetPassword: boolean;
|
useResetPassword: boolean;
|
||||||
useSecretsManager: boolean;
|
useSecretsManager: boolean;
|
||||||
|
useActivateAutofillPolicy: boolean;
|
||||||
selfHost: boolean;
|
selfHost: boolean;
|
||||||
usersGetPremium: boolean;
|
usersGetPremium: boolean;
|
||||||
seats: number;
|
seats: number;
|
||||||
|
@ -65,6 +66,7 @@ export class ProfileOrganizationResponse extends BaseResponse {
|
||||||
this.useCustomPermissions = this.getResponseProperty("UseCustomPermissions") ?? false;
|
this.useCustomPermissions = this.getResponseProperty("UseCustomPermissions") ?? false;
|
||||||
this.useResetPassword = this.getResponseProperty("UseResetPassword");
|
this.useResetPassword = this.getResponseProperty("UseResetPassword");
|
||||||
this.useSecretsManager = this.getResponseProperty("UseSecretsManager");
|
this.useSecretsManager = this.getResponseProperty("UseSecretsManager");
|
||||||
|
this.useActivateAutofillPolicy = this.getResponseProperty("UseActivateAutofillPolicy");
|
||||||
this.selfHost = this.getResponseProperty("SelfHost");
|
this.selfHost = this.getResponseProperty("SelfHost");
|
||||||
this.usersGetPremium = this.getResponseProperty("UsersGetPremium");
|
this.usersGetPremium = this.getResponseProperty("UsersGetPremium");
|
||||||
this.seats = this.getResponseProperty("Seats");
|
this.seats = this.getResponseProperty("Seats");
|
||||||
|
|
|
@ -21,7 +21,7 @@ export class PolicyService implements InternalPolicyServiceAbstraction {
|
||||||
policies$ = this._policies.asObservable();
|
policies$ = this._policies.asObservable();
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private stateService: StateService,
|
protected stateService: StateService,
|
||||||
private organizationService: OrganizationService
|
private organizationService: OrganizationService
|
||||||
) {
|
) {
|
||||||
this.stateService.activeAccountUnlocked$
|
this.stateService.activeAccountUnlocked$
|
||||||
|
|
|
@ -2365,6 +2365,26 @@ export class StateService<
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getActivateAutoFillOnPageLoadFromPolicy(options?: StorageOptions): Promise<boolean> {
|
||||||
|
return (
|
||||||
|
await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()))
|
||||||
|
)?.settings?.activateAutoFillOnPageLoadFromPolicy;
|
||||||
|
}
|
||||||
|
|
||||||
|
async setActivateAutoFillOnPageLoadFromPolicy(
|
||||||
|
value: boolean,
|
||||||
|
options?: StorageOptions
|
||||||
|
): Promise<void> {
|
||||||
|
const account = await this.getAccount(
|
||||||
|
this.reconcileOptions(options, await this.defaultOnDiskLocalOptions())
|
||||||
|
);
|
||||||
|
account.settings.activateAutoFillOnPageLoadFromPolicy = value;
|
||||||
|
return await this.saveAccount(
|
||||||
|
account,
|
||||||
|
this.reconcileOptions(options, await this.defaultOnDiskLocalOptions())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
async getSMOnboardingTasks(
|
async getSMOnboardingTasks(
|
||||||
options?: StorageOptions
|
options?: StorageOptions
|
||||||
): Promise<Record<string, Record<string, boolean>>> {
|
): Promise<Record<string, Record<string, boolean>>> {
|
||||||
|
|
Loading…
Reference in New Issue