Skip to content

[CXH-1572] - Add license management via workspace roles - Notion Connector#21

Open
mateoHernandez123 wants to merge 2 commits into
mainfrom
mateoHernandez123/add-roles-licences-resource
Open

[CXH-1572] - Add license management via workspace roles - Notion Connector#21
mateoHernandez123 wants to merge 2 commits into
mainfrom
mateoHernandez123/add-roles-licences-resource

Conversation

@mateoHernandez123

Copy link
Copy Markdown

Description

  • Bug fix
  • New feature

Adds Notion workspace roles as a first-class C1 Role resource with grant / revoke provisioning, plus an in-process SCIM mock server so local dev and CI can run without an Enterprise Notion tenant. Closes CXH-1572 — baton-notion: add license management.

Notion's SCIM API does not expose a /Licenses endpoint — "license" in Notion product terminology means "paid seat" and is binary (member vs. guest), not tiered. The connector therefore models the workspace role itself as the license tier: each Role resource carries TRAIT_LICENSE_PROFILE alongside TRAIT_ROLE (Adobe-style hybrid, mirroring baton-adobe #34), so every role doubles as a seat tier in C1 App Utilization.

Sync:

  • Users (user) — unchanged surface. Internally the SCIM extension's role value is stashed on the user profile during List so role grants can be emitted principal-side from userBuilder.Grants (one grant per user) without re-paginating users per role. Keeps sync at O(users) instead of O(users × roles).
  • Groups (group) — unchanged surface.
  • Roles (role, NEW) — static set of four (owner, membership_admin, member, restricted_member) matching the enum on the urn:ietf:params:scim:schemas:extension:notion:2.0:User.role extension. Per-resource Entitlements (not StaticEntitlements) because each role's display name and description vary — matches the canonical TRAIT_ROLE pattern. Declares TRAIT_LICENSE_PROFILE with WithLicenseEntitlementIDs so seat-holders correlate to their grants for App Utilization. Seat counts (WithLicenseSeats) intentionally omitted — Notion's SCIM API does not expose a per-role purchased / consumed endpoint.

Provisioning:

  • Create / Delete user accounts — unchanged.
  • Grant / Revoke role assignment — NEW. Both go through PATCH /scim/v2/Users/{id} on the Notion role extension. Idempotent via a single pre-flight GET /Users/{id}:
    • GrantAlreadyExists when the user already holds the target role (no PATCH issued).
    • GrantAlreadyRevoked on three short-circuits: (a) the user is gone (404), (b) the user no longer holds the role, (c) the role being revoked is restricted_member — Notion users must always carry a role, so revoke is modelled as a downgrade to restricted_member and the floor tier has nothing to downgrade to (account must be deprovisioned to remove the user from the workspace).

Auth:

Unchanged. Existing --scim-token flag, no new credentials, no breaking changes for existing deployments.

Architecture highlights:

  • Hybrid trait shape (TRAIT_ROLE + TRAIT_LICENSE_PROFILE). Lets the same resource power both UAR / JIT flows and App Utilization without inventing a second /Licenses resource for an API that doesn't have one.
  • Principal-side grant emission for the derived role type — roleBuilder.Grants returns nil intentionally; the actual grants come from userBuilder.Grants, which reads the stashed workspace_role profile field. Avoids the classic O(N × M) Grants scan.
  • Floor-tier Revoke short-circuit. Revoking restricted_member is a no-op (warn-logged) with GrantAlreadyRevoked — there is no lower tier to downgrade to.
  • New pkg/client/helpers.go centralizing API error classification (IsNotFoundError against the gRPC NotFound status that uhttp.BaseHttpClient maps from HTTP 404).
  • In-process SCIM mock under test-server/ (HTTP server + Postman collection + README), since Notion's SCIM API is Enterprise-only. The mock implements GET/POST/PATCH/DELETE /Users, GET /Groups, ServiceProviderConfig, and ResourceTypes, with stable seed IDs for CI assertions and both SCIM PATCH spellings (value-as-object and path) accepted on PATCH /Users/{id}.
  • Documentation refactored to the four-tier hierarchyREADME.md (developer), docs/connector.mdx (customer-facing, with AUTO-GENERATED markers for Generate Baton Metadata), docs/docs-info.md (internal engineering), test-server/README.md (mock).

Useful links:

@mateoHernandez123 mateoHernandez123 requested a review from a team June 5, 2026 15:16
@linear-code

linear-code Bot commented Jun 5, 2026

Copy link
Copy Markdown

CXH-1572

@github-actions

github-actions Bot commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

Connector PR Review: [CXH-1572] - Add license management via workspace roles - Notion Connector

Blocking Issues: 0 | Suggestions: 0 | Threads Resolved: 0
Review mode: incremental since fb46a0c
View review run

Review Summary

The incremental commit regenerates baton_capabilities.json to add the SkipGrants annotation on the role resource type, aligning the capabilities metadata with the code in resource_types.go (which already declares SkipGrants) and the intentionally-empty roleBuilder.Grants method. This is a correct metadata-only fix with no behavioral change. The two prior findings (misleading 409 comment in the Grant doc string at role.go:153, and missing unit tests for Grant/Revoke logic) remain unaddressed but are non-blocking suggestions.

Security Issues

None found.

Correctness Issues

None found.

Suggestions

None.

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

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.

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.

3 participants