Skip to content

Only emit user grants (where we emit the license grants) if license resource type is enabled#32

Open
laurenleach wants to merge 2 commits into
mainfrom
license-resource-type-check-user-grants
Open

Only emit user grants (where we emit the license grants) if license resource type is enabled#32
laurenleach wants to merge 2 commits into
mainfrom
license-resource-type-check-user-grants

Conversation

@laurenleach

Copy link
Copy Markdown

Only emit user grants (where we emit the license resource grants) if the license resource type is enabled.

Same shape as ConductorOne/baton-linear#55, applied to Zoom's derived License resource type.

Why

Zoom exposes no /licenses endpoint, so License is a derived resource type: its grants are emitted principal-side from userBuilder.Grants using the User.type tier stashed in the user profile during List. When a customer's sync filter excludes the license resource type, those grants would target resources that were never listed.

What

  • main.go derives syncLicenses from cli.ConnectorOpts.WillSyncResourceType(connector.LicenseResourceTypeID) — true when licenses are explicitly selected or when no filter is set at all (e.g. local CLI runs) — and passes it to connector.New.
  • userBuilder now clones resourceTypeUser and sets the skip annotation per-run instead of mutating the shared static var:
    • licenses synced → SkipEntitlements (unchanged behavior; users have no entitlements of their own)
    • licenses excluded → SkipEntitlementsAndGrants (license grants are the only grants users emit)
  • LicenseResourceTypeID is exported so main.go can reference it.
  • Regenerated metadata: baton_capabilities.json and config_schema.json are byte-identical to main, since the capabilities stub (NewForCapabilities, zero-value skipLicenseGrants) still advertises SkipEntitlements on the user resource type.
  • docs/connector.mdx: wrapped the capabilities table in the AUTO-GENERATED markers (the table contents already matched baton_capabilities.json) and documented the derived-license grant behavior.

Testing

  • go build ./cmd/baton-zoom, go vet ./..., go test -mod=vendor ./... all pass.
  • Verified the annotation wiring both ways with a throwaway test: the clone carries CapabilityPermissions plus exactly one skip annotation (SkipEntitlements when skipLicenseGrants=false, SkipEntitlementsAndGrants when true), and the shared resourceTypeUser var is left unmutated.
  • docs/connector.mdx compiles with @mdx-js/mdx.

🤖 Generated with Claude Code

…license resource type is enabled

Zoom has no /licenses endpoint, so License is a derived resource type whose
grants are emitted principal-side from userBuilder.Grants. When a customer's
sync filter excludes the license resource type, those grants would target
resources that were never listed, so the user syncer skips its grants pass
entirely via SkipEntitlementsAndGrants.

Users have no entitlements of their own, so the user resource type keeps
skipping the entitlements pass in both cases. The skip annotation now lives on
a per-builder clone of resourceTypeUser rather than the shared static var.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@laurenleach
laurenleach requested a review from a team July 24, 2026 20:14
Comment thread docs/connector.mdx

{/* AUTO-GENERATED:END - capabilities */}

Licenses are a derived resource type — Zoom exposes no `/licenses` endpoint, so license grants are emitted from the account sync using each user's Zoom license tier. If licenses are excluded from the connector's sync, accounts skip their grants pass entirely, since license grants are the only grants accounts emit.

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.

🟡 Suggestion: This paragraph says license grants come "from the account sync" and that "accounts skip their grants pass," but the grants are actually emitted principal-side from userBuilder.Grants (see pkg/connector/user.go), and there is no account resource syncer. To match the code (and this PR's own description), it should read "user sync" / "users" instead of "account sync" / "accounts."

@github-actions

Copy link
Copy Markdown
Contributor

Connector PR Review: Only emit user grants (where we emit the license grants) if license resource type is enabled

Blocking Issues: 0 | Suggestions: 2 | Threads Resolved: 0
Criteria: loaded .claude/skills/ci-review.md from trusted base 08d3fa8a0b87
Review mode: full
View review run

Review Summary

Scanned the full PR diff for security and correctness. The core change is sound: userBuilder now clones resourceTypeUser per-run and sets SkipEntitlementsAndGrants when the license resource type is excluded (WillSyncResourceType correctly defaults to true when no sync filter is set), avoiding grants that dangle onto never-listed License resources. The shared static var is no longer mutated, and NewForCapabilities keeps the byte-identical SkipEntitlements default. No blocking issues found; two suggestions on docs accuracy and test coverage.

Security Issues

None found.

Correctness Issues

None found.

Suggestions

  • docs/connector.mdx:29 - New paragraph attributes license grants to the "account sync" / "accounts," but they are emitted principal-side from userBuilder.Grants and there is no account syncer; should say "user sync" / "users."
  • pkg/connector/user.go:295 - No permanent test covers the new annotation wiring (SkipEntitlements vs SkipEntitlementsAndGrants); the PR verified it with a throwaway test only. Consider adding a table-driven test.
Prompt for AI agents
Verify each finding against the current code and only fix it if needed.

Suggestions:

In docs/connector.mdx:
- Around line 29: The paragraph states license grants are "emitted from the account sync" and that "accounts skip their grants pass entirely, since license grants are the only grants accounts emit." This is inaccurate: license grants are emitted principal-side from userBuilder.Grants in pkg/connector/user.go, and there is no account resource syncer. Reword to reference the user sync / users instead of account sync / accounts.

In pkg/connector/user.go:
- Around line 295: The new skipLicenseGrants annotation-wiring logic in userBuilder has no permanent test. Add a table-driven test asserting the cloned resource type carries CapabilityPermissions plus exactly one skip annotation (SkipEntitlements when skipLicenseGrants=false, SkipEntitlementsAndGrants when true), and that the shared resourceTypeUser var is left unmutated.

@github-actions github-actions 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.

No blocking issues found.

Recent baton-sdk versions moved profile, status, icon, and created_at from the
individual traits to attributes on Resource, deprecating the trait-level
options. CI's staticcheck flags the old ones as SA1019.

Following the same migration as ConductorOne/baton-procore#43:
  WithUserProfile / WithGroupProfile / WithRoleProfile -> WithResourceProfile
  WithStatus(v2.UserTrait_Status_STATUS_*)             -> WithResourceStatus(v2.Status_RESOURCE_STATUS_*, "")
  userTrait.GetProfile()                               -> resource.GetProfile(res)

The trait options move out of the trait slice and into the variadic
ResourceOption list. Traits themselves are unchanged: users keep their email
trait, groups keep GroupTrait, roles keep RoleTrait. resource.GetProfile falls
back to the deprecated trait-level profile, so reads still work against data
written by earlier connector versions.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

Connector PR Review: Only emit user grants (where we emit the license grants) if license resource type is enabled

Blocking Issues: 0 | Suggestions: 2 | Threads Resolved: 0
Criteria: Criteria status: loaded .claude/skills/ci-review.md from trusted base 08d3fa8a0b87.
Review mode: full
View review run

Review Summary

Scanned the full PR diff for security and correctness. The license-grant gating is sound: userBuilder clones resourceTypeUser via proto.Clone and adds SkipEntitlements (licenses synced) or SkipEntitlementsAndGrants (licenses excluded) without mutating the shared static var, and syncLicenses is correctly derived from WillSyncResourceType (true when no explicit filter is set). The trait-to-resource profile/status migration (WithResourceProfile / WithResourceStatus / GetProfile) uses APIs present in the vendored SDK, and a nil GetProfile result is safe because a nil struct AsMap call yields an empty map. No new issues found; the two suggestions below were flagged in a prior review and remain unaddressed in the current diff.

Security Issues

None found.

Correctness Issues

None found.

Suggestions

  • docs/connector.mdx:29 (carried over from prior review): Prose attributes license grants to the account sync / accounts, but they are emitted principal-side from userBuilder.Grants (the user syncer); consider user sync / users for accuracy.
  • pkg/connector/user.go:290 (carried over from prior review): No permanent test covers the new annotation wiring (SkipEntitlements vs SkipEntitlementsAndGrants, and that the shared resourceTypeUser var is left unmutated); a table-driven test would lock in the behavior.
Prompt for AI agents
Verify each finding against the current code and only fix it if needed.

Suggestions:

In docs/connector.mdx around line 29: The paragraph says license grants are emitted from the account sync and that accounts skip their grants pass entirely. License grants are actually emitted from userBuilder.Grants (the user syncer). Reword to attribute the grants to the user sync / users so the docs match the implementation.

In pkg/connector/user.go around line 290 (userBuilder): Add a permanent table-driven test asserting that userBuilder produces a resource type carrying CapabilityPermissions plus exactly one skip annotation - SkipEntitlements when skipLicenseGrants=false and SkipEntitlementsAndGrants when skipLicenseGrants=true - and that the shared package-level resourceTypeUser var is left unmutated after building.

@github-actions github-actions 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.

No blocking issues found.

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.

1 participant