Skip to content

Commit cdfa618

Browse files
authored
Introduce policy Sync to customize sync state (#313)
1 parent a2fa68b commit cdfa618

File tree

13 files changed

+271
-82
lines changed

13 files changed

+271
-82
lines changed

browser/components/enterprise/EnterpriseHandler.sys.mjs

Lines changed: 3 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,6 @@ ChromeUtils.defineESModuleGetters(lazy, {
1515
BrowserUtils: "resource://gre/modules/BrowserUtils.sys.mjs",
1616
ConsoleClient: "resource:///modules/enterprise/ConsoleClient.sys.mjs",
1717
EnterpriseCommon: "resource:///modules/enterprise/EnterpriseCommon.sys.mjs",
18-
UIState: "resource://services-sync/UIState.sys.mjs",
19-
Weave: "resource://services-sync/main.sys.mjs",
2018
});
2119

2220
ChromeUtils.defineLazyGetter(lazy, "log", () => {
@@ -27,7 +25,6 @@ ChromeUtils.defineLazyGetter(lazy, "log", () => {
2725
});
2826

2927
const PROMPT_ON_SIGNOUT_PREF = "enterprise.promptOnSignout";
30-
const ENTERPRISE_SYNC_ENABLED_PREF = "enterprise.sync.enabledByDefault";
3128

3229
export const EnterpriseHandler = {
3330
/**
@@ -36,16 +33,15 @@ export const EnterpriseHandler = {
3633
_signedInUser: null,
3734

3835
/**
39-
* Whether the handler is initialized, hence we have retrieved the
40-
* user information and initialized the sync state.
36+
* Whether the handler is initialized, meaning the user information
37+
* from the signed in user has been received from the console.
4138
*/
4239
_isInitialized: false,
4340

4441
/**
4542
* Handles the enterprise state for each new browser window.
4643
* On first call:
4744
* - Make a request to the console to retrieve the user information of the signed in user.
48-
* - Configure sync to be enabled or disable (depending on ENTERPRISE_SYNC_ENABLED_PREF)
4945
* On every call:
5046
* - Hide FxA toolbar button and FxA item in app menu (hamburger menu)
5147
*
@@ -55,78 +51,10 @@ export const EnterpriseHandler = {
5551
if (!this._isInitialized) {
5652
lazy.log.debug("Initializing...");
5753
await this.initUser();
58-
this.setupSyncOnceInitialized(window);
54+
this._isInitialized = true;
5955
}
6056
this.updateBadge(window);
6157
this.restrictEnterpriseView(window);
62-
this._isInitialized = true;
63-
},
64-
65-
/**
66-
* Check if the FxA state is initialised yet.
67-
* - If the state is still undefined, listen for a state update
68-
* and set up once the state update occurs.
69-
* - If the state is initialized, set up sync immediately.
70-
*
71-
* @param {Window} window chrome window
72-
*/
73-
setupSyncOnceInitialized(window) {
74-
const status = lazy.UIState.get().status;
75-
if (status === lazy.UIState.get().STATUS_NOT_CONFIGURED) {
76-
// State not configured yet.
77-
lazy.log.debug("Waiting for FxA/Sync status to be updated");
78-
const syncStateObserver = (_, topic) => {
79-
switch (topic) {
80-
case lazy.UIState.ON_UPDATE:
81-
lazy.log.debug("Sync state has been initialized");
82-
this.setUpSync(window);
83-
Services.obs.removeObserver(
84-
syncStateObserver,
85-
lazy.UIState.ON_UPDATE
86-
);
87-
break;
88-
default:
89-
break;
90-
}
91-
};
92-
Services.obs.addObserver(syncStateObserver, lazy.UIState.ON_UPDATE);
93-
return;
94-
}
95-
this.setUpSync();
96-
},
97-
98-
/**
99-
* Align sync state with expected state (ENTERPRISE_SYNC_ENABLED_PREF)
100-
* by enabling or disabling sync.
101-
*
102-
* @param {Window} window chrome window
103-
*/
104-
setUpSync(window) {
105-
lazy.log.debug("Handling sync state.");
106-
const isSyncCurrentlyEnabled = lazy.UIState.get().syncEnabled;
107-
const isEnableSync = Services.prefs.getBoolPref(
108-
ENTERPRISE_SYNC_ENABLED_PREF,
109-
false
110-
);
111-
112-
if (isSyncCurrentlyEnabled === isEnableSync) {
113-
// Nothing to do
114-
lazy.log.debug(
115-
`Not changing sync state. It was already ${isSyncCurrentlyEnabled ? "enabled" : "disabled"}`
116-
);
117-
return;
118-
}
119-
120-
if (isEnableSync) {
121-
lazy.log.debug(`Connect sync.`);
122-
lazy.Weave.Service.configure();
123-
} else {
124-
lazy.log.debug(`Disconnect sync.`);
125-
window.gSync.disconnect({
126-
confirm: false,
127-
disconnectAccount: false,
128-
});
129-
}
13058
},
13159

13260
async initUser() {

browser/components/enterprisepolicies/Policies.sys.mjs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ ChromeUtils.defineESModuleGetters(lazy, {
3333
WebsiteFilter: "resource:///modules/policies/WebsiteFilter.sys.mjs",
3434
});
3535

36-
const PREF_LOGLEVEL = "browser.policies.loglevel";
36+
export const PREF_LOGLEVEL = "browser.policies.loglevel";
3737
const BROWSER_DOCUMENT_URL = AppConstants.BROWSER_CHROME_URL;
3838
const ABOUT_CONTRACT = "@mozilla.org/network/protocol/about;1?what=";
3939

@@ -3410,6 +3410,11 @@ export var PoliciesUtils = {
34103410
restoreDefaultPref(prefName) {
34113411
const values = this._savedPrefs[prefName];
34123412

3413+
if (!values) {
3414+
// No default values available.
3415+
return;
3416+
}
3417+
34133418
let defaults = Services.prefs.getDefaultBranch("");
34143419
switch (typeof values.defaultValue) {
34153420
case "number":
@@ -3926,3 +3931,17 @@ function processMIMEInfo(mimeInfo, realMIMEInfo) {
39263931
}
39273932
lazy.gHandlerService.store(realMIMEInfo);
39283933
}
3934+
3935+
if (AppConstants.MOZ_ENTERPRISE) {
3936+
ChromeUtils.defineESModuleGetters(lazy, {
3937+
SyncPolicy: "resource:///modules/policies/SyncPolicy.sys.mjs",
3938+
});
3939+
Policies.Sync = {
3940+
async onBeforeAddons(manager, param) {
3941+
await lazy.SyncPolicy.applySettings(manager, param);
3942+
},
3943+
async onRemove(manager, _) {
3944+
await lazy.SyncPolicy.restoreSettings(manager);
3945+
},
3946+
};
3947+
}
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
/* This Source Code Form is subject to the terms of the Mozilla Public
2+
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
3+
* You can obtain one at http://mozilla.org/MPL/2.0/. */
4+
5+
import {
6+
PREF_LOGLEVEL,
7+
setAndLockPref,
8+
unsetAndUnlockPref,
9+
PoliciesUtils,
10+
} from "resource:///modules/policies/Policies.sys.mjs";
11+
12+
import { STATUS_OK as SYNC_STATUS_OK } from "resource://services-sync/constants.sys.mjs";
13+
14+
const lazy = {};
15+
ChromeUtils.defineESModuleGetters(lazy, {
16+
Weave: "resource://services-sync/main.sys.mjs",
17+
});
18+
19+
ChromeUtils.defineLazyGetter(lazy, "log", () => {
20+
return console.createInstance({
21+
prefix: "SyncPolicy",
22+
maxLogLevelPref: PREF_LOGLEVEL,
23+
});
24+
});
25+
26+
const ENGINE_PREFS = {
27+
Addresses: "services.sync.engine.addresses",
28+
Addons: "services.sync.engine.addons",
29+
Bookmarks: "services.sync.engine.bookmarks",
30+
History: "services.sync.engine.history",
31+
OpenTabs: "services.sync.engine.tabs",
32+
Passwords: "services.sync.engine.passwords",
33+
PaymentMethods: "services.sync.engine.creditcards",
34+
Settings: "services.sync.engine.prefs",
35+
};
36+
37+
const SYNC_FEATURE = "change-sync-state";
38+
39+
/**
40+
* Customizes Sync settings (all settings are optional):
41+
* - Whether sync is enabled/disabled
42+
* - Which types of data to sync
43+
* - Whether to lock the sync customization
44+
* See SyncPolicyParams for details.
45+
*/
46+
export const SyncPolicy = {
47+
/**
48+
* Get current sync state.
49+
*
50+
* @returns {boolean} Whether sync is currently enabled.
51+
*/
52+
isSyncEnabled() {
53+
return lazy.Weave.Status.checkSetup() == SYNC_STATUS_OK;
54+
},
55+
56+
/**
57+
* @typedef {object} SyncPolicyParams
58+
* @property {boolean} [Enabled] Whether Sync should be enabled
59+
* @property {boolean} [addresses] Whether syncing addresses should be enabled
60+
* @property {boolean} [bookmarks] Whether syncing bookmarks should be enabled
61+
* @property {boolean} [history] Whether syncing history should be enabled
62+
* @property {boolean} [openTabs] Whether syncing openTabs should be enabled
63+
* @property {boolean} [passwords] Whether syncing passwords should be enabled
64+
* @property {boolean} [paymentMethods] Whether syncing paymentMethods should be enabled
65+
* @property {boolean} [addons] Whether syncing addons should be enabled
66+
* @property {boolean} [settings] Whether syncing settings should be enabled
67+
* @property {boolean} [Locked] Whether to lock the customized sync settings
68+
*/
69+
70+
/**
71+
* Apply Sync settings
72+
*
73+
* @param {EnterprisePoliciesManager} manager
74+
* @param {SyncPolicyParams} params
75+
*
76+
* @returns {Promise<void>} Resolves once all Sync settings have been applied.
77+
*/
78+
async applySettings(manager, params) {
79+
lazy.log.debug("Apply Sync Settings");
80+
81+
// This might be an update to the Sync policy
82+
// so restore previous sync settings
83+
this.restoreSettings(manager);
84+
85+
const {
86+
Enabled: shouldEnableSync,
87+
Locked: shouldLock,
88+
...typeSettings
89+
} = params;
90+
91+
const isSyncEnabled = this.isSyncEnabled();
92+
93+
if (shouldEnableSync === true) {
94+
lazy.log.debug("Enable Sync");
95+
if (!isSyncEnabled) {
96+
await this.connectSync(manager);
97+
}
98+
} else if (shouldEnableSync === false) {
99+
lazy.log.debug("Disable Sync");
100+
if (isSyncEnabled) {
101+
await this.disconnectSync(manager);
102+
}
103+
}
104+
105+
for (const [type, value] of Object.entries(typeSettings)) {
106+
const pref = ENGINE_PREFS[type];
107+
if (shouldLock) {
108+
lazy.log.debug(`Setting and locking ${type}: ${pref} : ${value}`);
109+
setAndLockPref(pref, value);
110+
continue;
111+
}
112+
lazy.log.debug(`Setting ${type}: ${pref} : ${value}`);
113+
PoliciesUtils.setDefaultPref(pref, value, false);
114+
}
115+
116+
// Only lock the Sync feature if 'Enabled' is configured
117+
if (shouldLock && shouldEnableSync !== undefined) {
118+
manager.disallowFeature(SYNC_FEATURE);
119+
}
120+
},
121+
122+
/**
123+
* Restore initial sync state.
124+
*
125+
* @param {EnterprisePoliciesManager} manager
126+
*/
127+
async restoreSettings(manager) {
128+
if (!Services.policies.isAllowed(SYNC_FEATURE)) {
129+
manager.allowFeature(SYNC_FEATURE);
130+
}
131+
for (const pref of Object.values(ENGINE_PREFS)) {
132+
lazy.log.debug(`Unsetting ${pref}`);
133+
unsetAndUnlockPref(pref);
134+
}
135+
136+
// We don't have a way yet to restore the pre-policy sync
137+
// state (Bug 2017719). So for now we fallback to sync enabled.
138+
this.connectSync();
139+
},
140+
141+
/**
142+
* Disconnect sync
143+
*/
144+
async disconnectSync() {
145+
try {
146+
await lazy.Weave.Service.promiseInitialized;
147+
await lazy.Weave.Service.startOver();
148+
} catch (e) {
149+
lazy.log.error(`Failed to disconnect sync: ${e}`);
150+
}
151+
},
152+
153+
/**
154+
* Connect sync
155+
*/
156+
async connectSync() {
157+
try {
158+
await lazy.Weave.Service.configure();
159+
} catch (e) {
160+
lazy.log.error(`Failed to connect sync: ${e}`);
161+
}
162+
},
163+
};

browser/components/enterprisepolicies/helpers/moz.build

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@ EXTRA_JS_MODULES.policies += [
1212
"ProxyPolicies.sys.mjs",
1313
]
1414

15+
if CONFIG["MOZ_ENTERPRISE"]:
16+
EXTRA_JS_MODULES.policies += [
17+
"SyncPolicy.sys.mjs",
18+
]
19+
1520
EXTRA_PP_JS_MODULES.policies += [
1621
"WebsiteFilter.sys.mjs",
1722
]

browser/components/enterprisepolicies/schemas/policies-schema.json

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1697,6 +1697,42 @@
16971697
"required": ["Title", "URL"]
16981698
},
16991699

1700+
"Sync": {
1701+
"type": "object",
1702+
"properties": {
1703+
"Enabled": {
1704+
"type": "boolean"
1705+
},
1706+
"Addons": {
1707+
"type": "boolean"
1708+
},
1709+
"Addresses": {
1710+
"type": "boolean"
1711+
},
1712+
"Bookmarks": {
1713+
"type": "boolean"
1714+
},
1715+
"History": {
1716+
"type": "boolean"
1717+
},
1718+
"OpenTabs": {
1719+
"type": "boolean"
1720+
},
1721+
"Passwords": {
1722+
"type": "boolean"
1723+
},
1724+
"PaymentMethods": {
1725+
"type": "boolean"
1726+
},
1727+
"Settings": {
1728+
"type": "boolean"
1729+
},
1730+
"Locked": {
1731+
"type": "boolean"
1732+
}
1733+
}
1734+
},
1735+
17001736
"TranslateEnabled": {
17011737
"type": "boolean"
17021738
},

browser/components/preferences/dialogs/syncChooseWhatToSync.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,10 @@ let gSyncChooseWhatToSync = {
2828
this._setupEventListeners();
2929
this._adjustForPrefs();
3030
let options = window.arguments[0];
31-
if (options.disconnectFun) {
31+
if (
32+
options.disconnectFun &&
33+
Services.policies.isAllowed("change-sync-state")
34+
) {
3235
// Offer 'Disconnect' functionality if it was provided
3336
document.addEventListener("dialogextra2", function () {
3437
options.disconnectFun().then(disconnected => {

browser/components/preferences/sync.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -910,6 +910,12 @@ var gSyncPane = {
910910
"connect-another-device"
911911
);
912912
connectAnotherDeviceLink.setAttribute("restricted-enterprise-view", true);
913+
914+
if (!Services.policies.isAllowed("change-sync-state")) {
915+
// Hide info box and "Turn on syncing..." button (visible when Sync is disabled)
916+
const syncOffBox = document.getElementById("syncNotConfigured");
917+
syncOffBox.setAttribute("restricted-enterprise-view", true);
918+
}
913919
},
914920

915921
_updateSyncNow(syncing) {

0 commit comments

Comments
 (0)