Skip to content

[CXH-2292] - Migrate to ConnectorBuilderV2 and gate cross-resource grants - Grafana Connector - #81

Merged
mateoHernandez123 merged 4 commits into
mainfrom
mateoHernandez123/grafana-connector-builder-v2-and-grant-gating
Aug 31, 2026
Merged

[CXH-2292] - Migrate to ConnectorBuilderV2 and gate cross-resource grants - Grafana Connector#81
mateoHernandez123 merged 4 commits into
mainfrom
mateoHernandez123/grafana-connector-builder-v2-and-grant-gating

Conversation

@mateoHernandez123

@mateoHernandez123 mateoHernandez123 commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Description

  • Bug fix
  • New feature

This migrates baton-grafana to the SDK V2 connector interfaces required by config.RunConnector. It also carries C1's selected resource types into the connector so cross-resource grants are emitted only when their target entitlement type is part of the sync.

The migration preserves the existing resource IDs and full-sync grant surface. Account provisioning and entitlement provisioning behavior are unchanged.

Intent

CXH-2292 acceptance criteria:

  • Connector builds with RunConnector + ConnectorBuilderV2.
  • Full sync (user, org, team, role, service_account) matches pre-migration IDs and grants.
  • Sync without org lists service accounts and emits no service-account-to-org grants.
  • Sync with org still emits immutable service-account org-role grants.
  • Sync without role lists teams and emits membership grants without calling /api/access-control/teams/{id}/roles.
  • Sync with role still emits team-to-role grants.
  • Cross-resource grants whose entitlement belongs to another resource type are gated before emission instead of being dropped by the ingest filter.

Fixes CXH-2292

Sync:

  • Users (user) — migrated to ResourceSyncerV2; resources and grants are unchanged.
  • Organizations (org) — migrated to ResourceSyncerV2; resources, entitlements, and grants are unchanged.
  • Teams (team) — migrated to ResourceSyncerV2; membership grants are always emitted, while the team-role page is scheduled only when role is selected.
  • Roles (role) — migrated to ResourceSyncerV2; resources and entitlements are unchanged.
  • Service accounts (service_account) — migrated to ResourceSyncerV2; accounts remain listed when org is excluded, while org entitlements and grants are skipped.

Provisioning:

  • Create/Delete user accounts — unchanged.
  • Grant/Revoke team membership — unchanged.
  • Grant/Revoke role assignment — unchanged.

Auth:

Authentication is unchanged: API tokens continue to select Grafana Cloud mode, while username/password credentials select self-hosted mode.

Architecture highlights:

  • Uses config.RunConnector with ConnectorBuilderV2 and V2 resource syncers.
  • Reads C1's resource-type selection from cli.ConnectorOpts.
  • Clones the service-account resource type before adding SkipEntitlementsAndGrants, leaving the shared type unchanged.
  • Omits a session store because this connector has no shared sync state.
  • Keeps SyncOpResults nil when there is no pagination or annotation data to return.

Verification:

  • GOFLAGS=-mod=vendor go build ./...
  • GOFLAGS=-mod=vendor golangci-lint run ./...
  • GOFLAGS=-mod=vendor go test ./...
  • Generated capabilities and config match the committed metadata.
  • Cloud full-sync output before and after migration is identical: 90 resources, 67 entitlements, and 39 grants.
  • Without org, service accounts remain present and their org grants drop to zero.
  • Without role, team membership remains present and the team-role endpoint receives zero calls.
  • Team membership grant/revoke was exercised twice and the tenant state was restored.

Useful links:

… grants

Carry C1's resource-type selection into sync so cross-resource grants are emitted only when their target entitlement types are scheduled.

Co-authored-by: Cursor <cursoragent@cursor.com>
@linear-code

linear-code Bot commented Aug 28, 2026

Copy link
Copy Markdown

CXH-2292

Comment thread pkg/connector/connector.go
Comment thread pkg/connector/service_accounts.go Outdated
@github-actions

github-actions Bot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Connector PR Review: [CXH-2292] - Migrate to ConnectorBuilderV2 and gate cross-resource grants - Grafana Connector

Blocking Issues: 0 | Suggestions: 2 | Threads Resolved: 0
Criteria: Criteria status: loaded .claude/skills/ci-review.md from trusted base a79efc6dc516.
Review mode: incremental since 3a02555
View review run

Review Summary

The full PR diff was scanned for security and correctness; the new commit (9cfba42) was additionally reviewed for suggestion-level issues. That commit centralizes service-account resource-type gating: the static SkipEntitlements annotation moves off the package-level type into serviceAccountResourceType(syncOrgs), the runtime syncOrgs short-circuit inside Grants is dropped in favor of SkipEntitlementsAndGrants on the cloned type, and the team-grants page dispatch is rewritten from a switch to equivalent if branches. The refactor checks out: proto.Clone still protects the package-level var, annotations.Update appends when the annotation is absent, the SDK reads skip annotations off the runtime ResourceType(), and the committed baton_capabilities.json still matches because the capabilities prototype (nil connector opts) resolves syncOrgs to true. The prior service_accounts.go:31 annotation-replacement finding is fixed; the two suggestions below are carried over from the previous review and are unchanged in the current code.

Security Issues

None found.

Correctness Issues

None found.

Suggestions

  • pkg/connector/connector.go:38 — (carried over, still open) willSyncResourceType returns true on an empty selection, but the role type is OptInRequired and off by default, so the team-role gate likely does not engage in the default configuration.
  • docs/connector.mdx:97 and docs/connector.mdx:114 — (carried over, still open) stale credential requirements: both still say teams.roles:read is required even with Role sync turned off, which is no longer true now that the team-role page is gated on the role type being in scope.
Prompt for AI agents
Verify each finding against the current code and only fix it if needed.

## Suggestions

In `pkg/connector/connector.go`:
- Around line 36-44: willSyncResourceType delegates to cli.ConnectorOpts.WillSyncResourceType,
  which returns true whenever SyncResourceTypeIDs is empty (meaning "sync everything the
  connector advertises"). The role resource type carries the OptInRequired annotation (see
  pkg/connector/resource_types.go line 29), so under an empty/implicit selection the connector
  treats roles as in scope, and teamBuilder then calls
  GET /api/access-control/teams/<id>/roles and emits team-to-role grants even when C1 has not
  opted the customer into role sync. Confirm whether C1 always sends an explicit
  sync-resource-types list when opt-in types exist; if it does not, gate the team-role page on
  an explicit selection instead - only treat role as in scope when
  connectorOpts.SyncFilterIsExplicit() is true and the selection set contains
  resourceTypeRole.Id, while keeping the current no-filter behavior for the capabilities
  prototype and local CLI runs.

In `docs/connector.mdx`:
- Around line 97: The sentence "Team sync reads each team's RBAC roles, so `teams.roles:read` is
  required even with Role sync turned off." is no longer accurate - teamBuilder.Grants now skips
  the team-role page entirely when the role type is out of scope. Rewrite it so
  `teams.roles:read` is only required when Role sync is enabled, and drop or rescope the
  following "A narrower token that previously synced only users/orgs may fail List after this
  upgrade." warning accordingly.
- Around line 114: Same staleness in the self-hosted tab: "On Grafana Enterprise that account
  also needs `teams.roles:read`, which team sync reads for every team" should be updated to make
  `teams.roles:read` conditional on Role sync being enabled.

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

Keep the org-excluded resource type as a strict extension of the advertised service account type when adding the stronger sync skip annotation.

Co-authored-by: Cursor <cursoragent@cursor.com>

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

@sergiocorral-conductorone sergiocorral-conductorone 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.

Automated review

Comment thread pkg/connector/teams.go Outdated
Comment thread pkg/connector/teams.go
Comment thread pkg/connector/teams.go Outdated
Keep restored grant cursors subject to the current resource-type selection and make optional V2 entitlement interfaces fail at compile time if their signatures drift.

Co-authored-by: Cursor <cursoragent@cursor.com>

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

Comment thread pkg/connector/teams.go Outdated
Comment thread pkg/connector/service_accounts.go
Comment thread pkg/connector/service_accounts.go
Comment thread pkg/connector/service_accounts.go Outdated
Comment thread pkg/connector/service_accounts.go Outdated
Let the SDK scheduler enforce service-account grant skips through one runtime annotation, avoiding redundant guards and overlapping skip instructions.

Co-authored-by: Cursor <cursoragent@cursor.com>

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

@FeliLucero1

Copy link
Copy Markdown

Followed up on the deep-code-review nits from earlier — all addressed in af44697 / 9cfba42 (switch→if/else in teams.go, the serviceAccountResourceType helper in service_accounts.go). Double-checked the extra simplification that came with it (dropping the in-Grants() syncOrgs guard now that the resource type carries SkipEntitlementsAndGrants) against the vendored SDK's shouldSkipEntitlementsAndGrants — that's the same gate the sync engine uses before it ever calls into the connector, so no regression there. Build + full test suite green on 9cfba420. Nothing else outstanding from my side.

@mateoHernandez123
mateoHernandez123 merged commit a875286 into main Aug 31, 2026
9 checks passed
@mateoHernandez123
mateoHernandez123 deleted the mateoHernandez123/grafana-connector-builder-v2-and-grant-gating branch August 31, 2026 20:30
return true
}

return g.connectorOpts.WillSyncResourceType(resourceTypeID)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Did some due-diligence on the SDK side before this gets approved (traced WillSyncResourceType, SkipEntitlementsAndGrants gating through the sequential + parallel syncer, and every site that populates SyncResourceTypeIDs). The core mechanism this PR relies on holds up — the resource-type-level SkipEntitlementsAndGrants annotation is checked by the syncer before it ever calls into the connector's Grants(), in both sync paths, independent of the coarse whole-sync skip flag.

One thing worth flagging though, not really actionable in this PR but worth being aware of: g.connectorOpts here is fixed at connector-process boot (populated from local CLI flags in daemon/service/gRPC mode — pkg/cli/commands.go:464/:670 in the vendored SDK). Unlike lambda mode (which reloads on a config-version header change) and unlike the SDK's own separate per-task sdkSync.WithSyncResourceTypes filter (pkg/tasks/c1api/full_sync.go:245-250), there's no per-task refresh here. So if a tenant changes their resource-type selection in the C1 UI without the connector daemon restarting, the SDK's own sync-type filter would correctly pick up the new selection, but willSyncResourceType here — and therefore the SA-org-grant / team-role-grant gating — could still be acting on the stale selection from boot time until a restart happens.

Might be worth a quick check with whoever owns the deployment story: does C1's orchestration guarantee a restart when a tenant's resource-type selection changes for a long-running (non-lambda) connector? If yes, this is a non-issue. If not, it's a pre-existing SDK/deployment-model gap this PR doesn't introduce and can't really fix on its own, but it's the sort of thing worth a follow-up ticket.

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.

8 participants