[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:
Jake Fink 2023-03-10 12:52:43 -05:00 committed by GitHub
parent e8faf12ac1
commit 7892834f97
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 141 additions and 3 deletions

View File

@ -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"
}, },

View File

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

View File

@ -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() {

View File

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

View File

@ -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"
}, },

View File

@ -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(),
]); ]);
} }
} }

View File

@ -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],
}) })

View File

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

View File

@ -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 {}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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