-
Notifications
You must be signed in to change notification settings - Fork 18
Refactor updating enterprise policies #321
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: enterprise-main
Are you sure you want to change the base?
Conversation
1rneh
commented
Jan 8, 2026
- Refactor updating remote policies
- Fix merging policies of a combined provider when remote policies are updated
toolkit/components/enterprisepolicies/EnterprisePoliciesParent.sys.mjs
Outdated
Show resolved
Hide resolved
toolkit/components/enterprisepolicies/EnterprisePoliciesParent.sys.mjs
Outdated
Show resolved
Hide resolved
| ); | ||
|
|
||
| if (!isValid) { | ||
| continue; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will cause the policy to be removed because it doesn't go in parsedPolicies, unclear if that is what you want.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point! I didn't change that behavior. But let's re-think this :)
We have two options when encountering invalid parameters:
- Policy is currently active: We can remove the policy or keep it active with unchanged parameters. I think I prefer the latter. It's seems more intentional. In both ways we can warn in the logs about invalid parameters.
- Policy is currently inactive: Do nothing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function is only called during initialization though so the policy cannot already be active here. The question is what happens later when the policies are updated if this invalid policy isn't listed in _parsedPolicies. It looks to me like you don't want it in _parsedPolicies because that would then cause us to try to call an onRemove handler for the policy even though we haven't actually applied it.
…emote-policies-update
|
Update: This patch includes:
Question: |
toolkit/components/enterprisepolicies/EnterprisePoliciesParent.sys.mjs
Outdated
Show resolved
Hide resolved
toolkit/components/enterprisepolicies/EnterprisePoliciesParent.sys.mjs
Outdated
Show resolved
Hide resolved
toolkit/components/enterprisepolicies/EnterprisePoliciesParent.sys.mjs
Outdated
Show resolved
Hide resolved
toolkit/components/enterprisepolicies/EnterprisePoliciesParent.sys.mjs
Outdated
Show resolved
Hide resolved
toolkit/components/enterprisepolicies/EnterprisePoliciesParent.sys.mjs
Outdated
Show resolved
Hide resolved
| }, | ||
|
|
||
| get status() { | ||
| return this._status; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Given how error prone updating _status is, maybe we can add some sanity checks here for inconsistent status?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is _status error prone?
(I have not had a look at the PR yet so this may be invalid) From past experience I ran several times in regressions I introduced with remote fetching that broke some policies. So I think we should try and run with both, but maybe not right now, and maybe we'd like to scope them in a flagged suite, maybe |
…or-remote-policies-update
From an offline conversation with @mkaply: Tests under |
…or-remote-policies-update
|
This PR is growing and entails quite a lot of entangled changes and therefore difficult to split into multiple patches:
Note: |
| }, | ||
| }; | ||
|
|
||
| let policyValue = POLICY_PARAM_STATE.DEFAULT; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Throughout this test you verify the state of the active policies but you should sprinkle in checks that policyValue has the expected state after updates.
| // Combine policies with primaryProvider taking precedence. | ||
| // We only do this for top level policies. | ||
| this._policies = this._primary._policies; | ||
| for (let policyName of Object.keys(this._secondary.policies)) { | ||
| this._policies = structuredClone(this._primaryProvider.policies); | ||
| for (let [policyName, policyParams] of Object.entries( | ||
| this._secondaryProvider.policies || {} | ||
| )) { | ||
| if (!(policyName in this._policies)) { | ||
| this._policies[policyName] = this._secondary.policies[policyName]; | ||
| this._policies[policyName] = policyParams; | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| // Combine policies with primaryProvider taking precedence. | |
| // We only do this for top level policies. | |
| this._policies = this._primary._policies; | |
| for (let policyName of Object.keys(this._secondary.policies)) { | |
| this._policies = structuredClone(this._primaryProvider.policies); | |
| for (let [policyName, policyParams] of Object.entries( | |
| this._secondaryProvider.policies || {} | |
| )) { | |
| if (!(policyName in this._policies)) { | |
| this._policies[policyName] = this._secondary.policies[policyName]; | |
| this._policies[policyName] = policyParams; | |
| } | |
| } | |
| // Combine policies with primaryProvider taking precedence. | |
| // We only do this for top level policies. | |
| return Object.assign({}, this._secondaryProvider.policies ?? {}, this._primaryProvider.policies); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is just a manual implementation of Object.assign
| _chooseProvider() { | ||
| let platformProvider = null; | ||
| if (AppConstants.platform == "win" && AppConstants.MOZ_SYSTEM_POLICIES) { | ||
| platformProvider = new WindowsGPOPoliciesProvider(); | ||
| platformProvider.onPoliciesChanges(handler); | ||
| } else if ( | ||
| AppConstants.platform == "macosx" && | ||
| AppConstants.MOZ_SYSTEM_POLICIES | ||
| ) { | ||
| platformProvider = new macOSPoliciesProvider(); | ||
| platformProvider.onPoliciesChanges(handler); | ||
| if (AppConstants.MOZ_SYSTEM_POLICIES) { | ||
| if (AppConstants.platform == "win") { | ||
| platformProvider = new WindowsGPOPoliciesProvider(); | ||
| } else if (AppConstants.platform == "macosx") { | ||
| platformProvider = new macOSPoliciesProvider(); | ||
| } | ||
| } | ||
|
|
||
| let jsonProvider = new JSONPoliciesProvider(); | ||
| jsonProvider.onPoliciesChanges(handler); | ||
| let remoteProvider = RemotePoliciesProvider.createInstance(); | ||
| remoteProvider.onPoliciesChanges(handler); | ||
| if (platformProvider && platformProvider.hasPolicies) { | ||
| if (jsonProvider.hasPolicies) { | ||
| return new CombinedProvider( | ||
| new CombinedProvider(remoteProvider, platformProvider), | ||
| jsonProvider | ||
| ); | ||
| return new CombinedProvider(platformProvider, jsonProvider); | ||
| } | ||
| return new CombinedProvider(remoteProvider, platformProvider); | ||
| return platformProvider; | ||
| } | ||
| if (jsonProvider.hasPolicies) { | ||
| return new CombinedProvider(remoteProvider, jsonProvider); | ||
| return jsonProvider; | ||
| }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You don't have to do it here but I think things would make a lot more sense if instead of having CombinedProvider merge two policies it could instead merge any number of policies. Then instead of a binary tree of policy providers there would just be a single CombinedProfiler holding all the real providers and it would be able to merge them all in a single operation.
| ); | ||
|
|
||
| if (!isValid) { | ||
| continue; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function is only called during initialization though so the policy cannot already be active here. The question is what happens later when the policies are updated if this invalid policy isn't listed in _parsedPolicies. It looks to me like you don't want it in _parsedPolicies because that would then cause us to try to call an onRemove handler for the policy even though we haven't actually applied it.
| ); | ||
|
|
||
| if (!isValid) { | ||
| console.warn(`Parameters for policy ${policyName} are invalid`); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
_validatePolicyParams already logs warnings in the event of invalid data so we don't need to do it here.
| break; | ||
|
|
||
| case "EnterprisePolicies:Reset": | ||
| this._resetEngine().then(null, console.error); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| this._resetEngine().then(null, console.error); | |
| this._resetEngine().catch(console.error); |
| /** | ||
| * Update engine status after parsing a new set of policies | ||
| */ | ||
| _updateStatus() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should set the status to FAILED if this._provider.failed
mkaply
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've asked some questions and made a few suggestions, but assuming this pasts the tests (and you've addressed Mossop's questions), this seems good.
| // of setAndLockPref, it wasn't locked, but calling | ||
| // unlockPref is harmless | ||
| Preferences.unlock(prefName); | ||
| if (Services.prefs.prefIsLocked(prefName)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why was this added? As said above, Preference.unlock is harmless?
| isTesting: "resource:///modules/enterprise/EnterpriseCommon.sys.mjs", | ||
| FeltCommon: "chrome://felt/content/FeltCommon.sys.mjs", | ||
| FeltStorage: "resource:///modules/FeltStorage.sys.mjs", | ||
| PREF_REMOTE_POLICIES_ENABLED: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the reason the same JS is loaded into two different identifiers?
| "DisableDefaultBrowserAgent", | ||
| "DisableForgetButton", | ||
| "DisableFormHistory", | ||
| "DisableRemoteImprovements", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is unrelated to this patch.