Skip to content

Add user-scoped declaration support#48796

Open
JordanMontgomery wants to merge 3 commits into
mainfrom
48567-user-scoped-ddm-declarations
Open

Add user-scoped declaration support#48796
JordanMontgomery wants to merge 3 commits into
mainfrom
48567-user-scoped-ddm-declarations

Conversation

@JordanMontgomery

@JordanMontgomery JordanMontgomery commented Jul 6, 2026

Copy link
Copy Markdown
Member

Related issue: Resolves #

Checklist for submitter

If some of the following don't apply, delete the relevant line.

  • Changes file added for user-visible changes in changes/, orbit/changes/ or ee/fleetd-chrome/changes.
    See Changes files for more information.

  • Input data is properly validated, SELECT * is avoided, SQL injection is prevented (using placeholders for values in statements), JS inline code is prevented especially for url redirects, and untrusted data interpolated into shell scripts/commands is validated against shell metacharacters.

  • Timeouts are implemented and retries are limited to avoid infinite loops

  • If paths of existing endpoints are modified without backwards compatibility, checked the frontend/CLI for any necessary changes

Testing

Summary by CodeRabbit

  • New Features
    • Added support for Apple declarative management declarations on both System and User channels.
    • User-scoped declarations are now delivered, reconciled, and acknowledged independently from device-scoped declarations.
  • Bug Fixes
    • Prevented scope-mixing so declaration items and status updates no longer affect the wrong channel.
    • Tightened reconciliation behavior for scope changes and missing user channels.
  • Tests
    • Expanded coverage for channel isolation, payload scope parsing/validation, and correct delivery payload behavior (including stripping the payload-scope field from delivered JSON).

@JordanMontgomery

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning

  • Copilot's review of this pull request may be incomplete because some of the changed files are excluded by your Copilot content exclusion settings. See Excluding content from Copilot for details.

Pull request overview

Adds support for Apple DDM “user-scoped” declarations by introducing a Fleet-only PayloadScope key that controls whether declarations are delivered on the device (System) or user (User) channel, and then updating reconcile + serving logic to keep the two channels isolated end-to-end.

Changes:

  • Parse/validate PayloadScope on upload, persist it as scope, and default missing scope to System for backward compatibility.
  • Scope DDM serving and status transitions by channel (tokens, declaration-items, declaration payloads, status reports, and DeclarativeManagement acks).
  • Update batched reconciliation to separately poke device vs user channels, including user-channel enrollment handling, grace-window holding, and cleanup for undeliverable user-scoped removals.

Reviewed changes

Copilot reviewed 16 out of 18 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
server/service/mdm.go Validate and persist declaration scope when building declarations via GitOps/batch paths.
server/service/integration_mdm_ddm_test.go Adds integration coverage that batch upload persists scope correctly (default + explicit User).
server/service/apple_mdm.go Scopes DDM serving/ack handling by channel; strips Fleet-only PayloadScope at delivery.
server/service/apple_mdm_declarations_batched.go Reconcile now partitions device/user changes and handles user-channel delivery decisions + cleanup.
server/service/apple_mdm_declarations_batched_test.go Unit tests for user-channel delivery decision logic (deliver/hold/fail/remove-delete).
server/service/apple_mdm_ddm_test.go Service-level tests for channel isolation (tokens/items) and delivery-time stripping of PayloadScope.
server/mock/datastore_mock.go Updates datastore mock for new scope-aware DDM methods and new bulk delete method.
server/mdm/apple/reconcile.go ComputeDeclarationDeltas now tracks changes per channel and handles scope flips.
server/mdm/apple/reconcile_test.go Adds/updates tests for per-channel delta computation and scope flip behavior.
server/fleet/datastore.go Datastore interface updated for scope-aware DDM queries and per-channel resync + bulk delete.
server/fleet/apple_mdm.go Adds scope fields + PayloadScope parsing/validation helpers on raw declarations and host decl rows.
server/fleet/apple_mdm_test.go Unit tests for PayloadScope parsing/defaulting/validation and host-decl equality update.
server/datastore/mysql/apple_mdm.go Persists scope for declarations, scopes DDM queries by channel, partitions resync by scope.
server/datastore/mysql/apple_mdm_test.go Updates tests for new scope-aware DDM token method + pending declarations transition signature.
server/datastore/mysql/apple_mdm_ddm_test.go Adds datastore-level test asserting device/user channel isolation across DDM queries and status reports.
server/datastore/mysql/apple_mdm_batched.go Includes scope in batched host declaration reads/writes and adds bulk delete helper.
changes/48567-user-scoped-declarations User-visible change note (content excluded from review).
docs/Contributing/architecture/mdm/apple-declarative-device-management.md Architecture docs update (content excluded from review).
Files excluded by content exclusion policy (2)
  • changes/48567-user-scoped-declarations
  • docs/Contributing/architecture/mdm/apple-declarative-device-management.md

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread server/service/apple_mdm.go
Comment thread server/service/apple_mdm.go Outdated
Comment on lines +6361 to +6364
// PayloadScope is a Fleet extension, not part of Apple's DDM schema. It's
// normally stripped at upload, but a declaration provided entirely as a
// $FLEET_SECRET_ token only resolves to JSON here, so strip it again after
// expansion to guarantee it never reaches the device.
Comment thread server/datastore/mysql/apple_mdm.go
@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 560f7132-e89f-44b8-8a60-8dc14f349e2f

📥 Commits

Reviewing files that changed from the base of the PR and between a03602b and fc8e13b.

⛔ Files ignored due to path filters (1)
  • docs/Contributing/architecture/mdm/apple-declarative-device-management.md is excluded by !**/*.md
📒 Files selected for processing (7)
  • pkg/mdm/mdmtest/apple.go
  • server/datastore/mysql/apple_mdm.go
  • server/fleet/apple_mdm.go
  • server/service/apple_mdm.go
  • server/service/apple_mdm_declarations_batched.go
  • server/service/integration_mdm_ddm_test.go
  • server/service/mdm.go
🚧 Files skipped from review as they are similar to previous changes (5)
  • server/fleet/apple_mdm.go
  • server/service/apple_mdm_declarations_batched.go
  • server/service/mdm.go
  • server/service/apple_mdm.go
  • server/datastore/mysql/apple_mdm.go

Walkthrough

This PR adds user-channel scoping for Apple MDM declarative management. It introduces PayloadScope on declarations, validates and persists the scope, and filters datastore reads and writes by host_uuid plus scope. Reconciliation now returns separate device and user change sets and handles scope flips. The service layer derives scope from enrollment context, strips the payload-scope field before delivery, and scopes status updates. Batched reconciliation and tests were updated for user-channel delivery, removal, and resync behavior.

Possibly related issues

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: adding user-scoped declaration support.
Description check ✅ Passed The description matches the template and covers the required checklist sections, with only the related issue line left blank.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 48567-user-scoped-ddm-declarations

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
server/datastore/mysql/apple_mdm.go (1)

4694-4734: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consolidate duplicated scope-defaulting logic.

The if scope == "" { scope = fleet.PayloadScopeSystem } pattern (with the identical "Scope defaults to System" comment) is duplicated in insertOrUpdateDeclarations (Line 4724-4727) and insertOrUpsertMDMAppleDeclaration (Line 4917-4920), and again in BulkUpsertMDMAppleHostDeclarations in apple_mdm_batched.go. Extracting a small helper (e.g. scopeOrDefault(scope fleet.PayloadScope) fleet.PayloadScope) would reduce triplication and the risk of the default drifting out of sync across call sites.

♻️ Proposed helper
func scopeOrDefault(scope fleet.PayloadScope) fleet.PayloadScope {
	if scope == "" {
		return fleet.PayloadScopeSystem
	}
	return scope
}

Also applies to: 4907-4927

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@server/datastore/mysql/apple_mdm.go` around lines 4694 - 4734, The
scope-defaulting logic is duplicated across insertOrUpdateDeclarations,
insertOrUpsertMDMAppleDeclaration, and BulkUpsertMDMAppleHostDeclarations, so
extract it into a small helper like scopeOrDefault and use that everywhere.
Replace each inline `if scope == "" { scope = fleet.PayloadScopeSystem }` block
with the shared helper to keep the default consistent and avoid drift.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@server/service/apple_mdm_declarations_batched.go`:
- Around line 111-113: The empty-declaration early return in
MDMAppleHostDeclarationsBatchWrite is bypassing resync handling, so resync-only
hosts never reach MDMAppleHostDeclarationsGetAndClearResync and keep their
resync flag set. Move the len(declRowsToWrite) == 0 return to after the
resync-processing block, ensuring the resync lookup/clear logic still runs even
when there are no declaration rows to write.

---

Nitpick comments:
In `@server/datastore/mysql/apple_mdm.go`:
- Around line 4694-4734: The scope-defaulting logic is duplicated across
insertOrUpdateDeclarations, insertOrUpsertMDMAppleDeclaration, and
BulkUpsertMDMAppleHostDeclarations, so extract it into a small helper like
scopeOrDefault and use that everywhere. Replace each inline `if scope == "" {
scope = fleet.PayloadScopeSystem }` block with the shared helper to keep the
default consistent and avoid drift.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: d0f290b0-515e-4e34-9fdb-f08c787b4c73

📥 Commits

Reviewing files that changed from the base of the PR and between bd465f5 and a03602b.

⛔ Files ignored due to path filters (1)
  • docs/Contributing/architecture/mdm/apple-declarative-device-management.md is excluded by !**/*.md
📒 Files selected for processing (17)
  • changes/48567-user-scoped-declarations
  • server/datastore/mysql/apple_mdm.go
  • server/datastore/mysql/apple_mdm_batched.go
  • server/datastore/mysql/apple_mdm_ddm_test.go
  • server/datastore/mysql/apple_mdm_test.go
  • server/fleet/apple_mdm.go
  • server/fleet/apple_mdm_test.go
  • server/fleet/datastore.go
  • server/mdm/apple/reconcile.go
  • server/mdm/apple/reconcile_test.go
  • server/mock/datastore_mock.go
  • server/service/apple_mdm.go
  • server/service/apple_mdm_ddm_test.go
  • server/service/apple_mdm_declarations_batched.go
  • server/service/apple_mdm_declarations_batched_test.go
  • server/service/integration_mdm_ddm_test.go
  • server/service/mdm.go

Comment thread server/service/apple_mdm_declarations_batched.go Outdated
@codecov

codecov Bot commented Jul 6, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 76.52733% with 73 lines in your changes missing coverage. Please review.
✅ Project coverage is 68.04%. Comparing base (8415ae4) to head (fc8e13b).
⚠️ Report is 20 commits behind head on main.

Files with missing lines Patch % Lines
server/service/apple_mdm_declarations_batched.go 72.17% 21 Missing and 11 partials ⚠️
server/datastore/mysql/apple_mdm_batched.go 46.66% 16 Missing ⚠️
server/datastore/mysql/apple_mdm.go 84.74% 2 Missing and 7 partials ⚠️
pkg/mdm/mdmtest/apple.go 74.19% 4 Missing and 4 partials ⚠️
server/service/apple_mdm.go 81.25% 4 Missing and 2 partials ⚠️
server/service/mdm.go 33.33% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #48796      +/-   ##
==========================================
+ Coverage   68.02%   68.04%   +0.01%     
==========================================
  Files        3681     3684       +3     
  Lines      233961   234281     +320     
  Branches    12453    12453              
==========================================
+ Hits       159146   159410     +264     
- Misses      60497    60536      +39     
- Partials    14318    14335      +17     
Flag Coverage Δ
backend 69.69% <76.52%> (+0.02%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@JordanMontgomery

Copy link
Copy Markdown
Member Author

@claude review once

@JordanMontgomery JordanMontgomery marked this pull request as ready for review July 6, 2026 20:07
@JordanMontgomery JordanMontgomery requested review from a team and georgekarrv as code owners July 6, 2026 20:07

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude Code Review

This repository is configured for manual code reviews. Comment @claude review to trigger a review and subscribe this PR to future pushes, or @claude review once for a one-time review.

Tip: disable this comment in your organization's Code Review settings.

Comment thread server/service/apple_mdm.go
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants