Conversation
e0beb6d to
24b18b8
Compare
9f1a6e7 to
ede09eb
Compare
7f0f940 to
0e837d9
Compare
|
Artifact containing the Flatpak bundle: com.nextcloud.desktopclient.nextcloud.flatpak.zip Digest: To test this change/fix you can download the above artifact file, unzip it, and install the bundle with: Please make sure to quit your existing Nextcloud app and backup your data. |
0e837d9 to
5d6f855
Compare
|
Artifact containing the AppImage: nextcloud-appimage-pr-10589.zip Digest: To test this change/fix you can download the above artifact file, unzip it, and run it. Please make sure to quit your existing Nextcloud app and backup your data. |
1. Existing in-place legacy configurations are no longer importedPriority: P1
The previous implementation continued with the already-open Suggested direction: Return the initially opened settings when its legacy group is non-empty, and add a regression test covering that configuration layout. 2. Account restoration erases the offline managed-settings cachePriority: P1 Restored accounts do not hydrate The merge therefore sees empty per-account maps and persists an empty aggregate over Suggested direction: Preserve the cached aggregate until fresh capabilities have successfully arrived, or persist and restore managed settings per account before merging them. 3. One enforced proxy field promotes unrelated defaults above user settingsPriority: P1
For example, if device policy enforces only Suggested direction: Preserve source and enforcement metadata per field and decide whether to override each field independently, or resolve the proxy configuration atomically from one source. 4. Failed type conversion is still returned as an enforced valuePriority: P1 The resolver selects the highest-priority source before converting its value to the schema type. It ignores the return value of Qt leaves a failed conversion as a cleared/null value of the target type. Consumers can consequently observe Suggested direction: Convert and validate each candidate before it participates in priority selection. If conversion fails, reject that candidate and continue with the next source. 5. Persisted server settings bypass the allowlist and enforcement policyPriority: P1 Live capability data is passed through A stale, future, or edited cache can therefore restore unknown keys or server-enforced update/proxy values that the live-capability path explicitly rejects. The cache also records Suggested direction: Apply schema-version validation and 6. The update settings UI disables the wrong controlPriority: P2 When The automatic-update switch therefore remains visually editable but has no connected handler. Meanwhile, the unrelated manual update action is disabled and can later be re-enabled by updater-state handling. Suggested direction: Disable the automatic-update switch and its label when 7. The folder-limit checkbox checks enforcement for the wrong keyPriority: P2
As a result:
Suggested direction: Bind the checkbox to 8. The migration test gives
|
nilsding
left a comment
There was a problem hiding this comment.
Thanks for building a solid foundation to manage settings in a centralised way 🎉
Some smaller comments from my end, other than that we're good to go
f8ea365 to
f1238e3
Compare
5600fde to
e4d153f
Compare
e4d153f to
b668fc6
Compare
d422fcf to
e7b39b0
Compare
claucambra
left a comment
There was a problem hiding this comment.
Some initial comments
| ## Sources and the resolver | ||
|
|
||
| A source is a small adapter over one place a value can come from. It holds no | ||
| values; it reads them live and tags them with a provenance, an enforcement state and | ||
| a priority: | ||
|
|
||
| ```cpp | ||
| class SettingSource { | ||
| public: | ||
| virtual std::optional<QVariant> read(const QString &key, const QString &group) const = 0; | ||
| virtual SettingSourceType type() const = 0; // PlatformPolicy, ServerDefault, ... | ||
| virtual EnforcementState enforcement() const = 0; // Enforced or NotEnforced | ||
| virtual int priority() const = 0; // who wins on a conflict | ||
| }; | ||
| ``` | ||
|
|
||
| buildDeviceSources and buildServerSources are factory functions that construct these | ||
| adapters for the current environment. The device set depends on the platform and | ||
| carries the priorities: | ||
|
|
||
| ```cpp | ||
| // buildDeviceSources, Windows | ||
| HKCU\Software\Policies\<vendor>\<app> PlatformPolicy, Enforced, 210 | ||
| HKLM\Software\Policies\<vendor>\<app> PlatformPolicy, Enforced, 200 | ||
| HKLM\Software\<vendor>\<app> PlatformDefault, NotEnforced, 20 | ||
| // macOS: MacForcedPreferenceSource(200) + /Library/Preferences/<domain>.plist (20) | ||
| // Linux: <sysconfdir>/<app>/policies.conf (enforced 200), <sysconfdir>/<app>/<app>.conf (20) | ||
| ``` | ||
|
|
||
| ```cpp | ||
| // buildServerSources, from the cached server settings | ||
| if (!sanitized.enforced.isEmpty()) { | ||
| sources.push_back(std::make_unique<ServerSettingsSource>( | ||
| sanitized.enforced, SettingSourceType::ServerEnforced, EnforcementState::Enforced, 100)); | ||
| } | ||
| if (!sanitized.defaults.isEmpty()) { | ||
| sources.push_back(std::make_unique<ServerSettingsSource>( | ||
| sanitized.defaults, SettingSourceType::ServerDefault, EnforcementState::NotEnforced, 30)); | ||
| } | ||
| ``` | ||
|
|
||
| getConfig builds the whole stack fresh on each call, so resolution reflects the | ||
| current registry, plist and cfg (which is why a device policy change is seen after | ||
| the dialog reopens): | ||
|
|
||
| ```cpp | ||
| ManagedSettings resolver; | ||
| for (auto &deviceSource : buildDeviceSources()) { | ||
| resolver.addSource(std::move(deviceSource)); | ||
| } | ||
| resolver.addSource(std::make_unique<UserConfigSource>(configFile(), groupName)); // 50 | ||
| resolver.addSource(std::make_unique<UserConfigSource>(configFile(), QString(), 49)); // legacy top level | ||
| for (auto &serverSource : buildServerSources(serverManagedSettings())) { | ||
| resolver.addSource(std::move(serverSource)); | ||
| } | ||
| return resolver.resolve(definition); | ||
| ``` | ||
|
|
||
| resolve then asks every source and keeps the highest priority one that has a value, | ||
| subject to the enforceable gate: | ||
|
|
||
| ```cpp | ||
| if (source->priority() > settingPriority) { | ||
| settingSource = source.get(); | ||
| settingValue = *value; | ||
| settingPriority = source->priority(); | ||
| } | ||
| ``` |
There was a problem hiding this comment.
IMO this is delving too deep into implementation details. I think these comments should be in the code and not in a broader README; it seems to me a README should instead more broadly describe the bird's eye view of what is happening
Add the managed settings model, source hierarchy, schema, server cache, and configuration gateway. Cover precedence, enforcement metadata, platform sources, and resolver behavior with tests. Assisted-by: GitHub Copilot:2026-09 Signed-off-by: Camila Ayres <hello@camilasan.com>
Keep update and proxy settings defaultable by the server while reserving enforcement for device policy. Route update access through the managed resolver and preserve the security boundary. Assisted-by: GitHub Copilot:2026-09 Signed-off-by: Camila Ayres <hello@camilasan.com>
Add folder limit definitions, server value validation, managed resolution, and enforcement aware controls for advanced settings. Assisted-by: GitHub Copilot:2026-09 Signed-off-by: Camila Ayres <hello@camilasan.com>
Validate virtual files modes and apply managed defaults and enforcement when creating folders through the setup and folder wizards. Assisted-by: GitHub Copilot:2026-09 Signed-off-by: Camila Ayres <hello@camilasan.com>
…ing more clear. Clarify managed settings names and present enforcement state in update, proxy, folder limit, and virtual files controls. Assisted-by: GitHub Copilot:2026-09 Signed-off-by: Camila Ayres <hello@camilasan.com>
Assisted-by: GitHub Copilot:2026-09 Assisted-by: Claude Code:claude-opus-4-8 Signed-off-by: Camila Ayres <hello@camilasan.com>
Move it to docs/settings folder. Renamed it to managed-settings.md. Signed-off-by: Camila Ayres <hello@camilasan.com>
Signed-off-by: Camila Ayres <hello@camilasan.com>
A policy cannot enforce a setting the client did not declare. Update the documentation with the new default. Assisted-by: Claude Code:claude-opus-4-8 Signed-off-by: Camila Ayres <hello@camilasan.com>
e7b39b0 to
22191af
Compare
|




Resolves
#5497
This PR is branched off #9191 (the first 4 commits).
Summary
Lets an administrator set defaults and enforce client settings across their users, through the Nextcloud server (Enterprise) and through device policy (Windows, macOS, Linux). Settings resolve through one enforcement aware path with a clear precedence, and enforced controls are disabled in the UI with a "Managed by ..." label.
How it works
ConfigFile::getConfigresolves each setting across a priority ordered stack: device enforced, server enforced, user, server default, device default, built in default.What can be managed
autoUpdateCheck,skipUpdateCheckconfirmExternalStorageuseNewBigFolderSizeLimit,newBigFolderSizeLimit,notifyExistingFoldersOverLimit,stopSyncingExistingFoldersOverLimitproxyType,proxyHost,proxyPortvirtualFilesModeKey rules
Tests and docs
test/testmanagedsettings.cppcovers the resolver, sanitize, schema, proxy merge and virtual files.src/libsync/settings/README.mddocuments the design.Server side
Delivery depends on the support app change (separate repo and PR) exposing
support.desktopClientin the capabilities, gated on an enterprise subscription.Checklist
AI (if applicable)