Registration.ExtraSettings was a single flat list, but the API-key and
client-credentials connect dialogs need different fields whenever a
provider offers both paths, because a different create resolver and a
different driver sits behind each. Replace it with
APIKeyExtraSettings and ClientCredentialsExtraSettings, and split the
GraphQL surface to match so a client cannot render one path's settings
on the other.
This fixes two connectors that could not be connected at all.
1Password declared accountId and region only, which are the
client-credentials shape. The API-key dialog therefore rendered those
two fields, mapAPIKeyExtraSettingToField returned nil for both so
buildExtraFields discarded them, and the SCIM-bridge driver failed on an
empty SCIMBridgeURL. The console already mapped scimBridgeUrl, but no
registration declared that key, so the branch was dead. It now declares
scimBridgeUrl on the API-key path and accountId + region on client
credentials.
Langfuse declared baseUrl as required, but mapAPIKeyExtraSettingToField
had no LANGFUSE case, so buildExtraFields dropped the value the customer
typed and the mutation failed with "langfuseBaseUrl is required". Every
other extra-settings provider had a case. The GraphQL input field, the
settings struct, the probe builder and the driver were all already
correct; only the console mapping was missing.
buildExtraFields now takes the settings list explicitly instead of
reading it off the provider, so each dialog passes its own path's list
and cannot silently iterate the other one.
Register rejects a settings list for a path the provider does not offer,
and an empty or duplicate setting key within one list. A key repeated
across the two lists is allowed: that is how a dual-path provider
declares a setting both dialogs need.
The new resolver tests walk the whole chain the console walks, from the
key a Registration declares through the mutation input field to the
persisted settings struct, so a key renamed on one side and not the
other fails in CI instead of at connect time.
Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
Two OAuth2 and two API-key connectors:
- Google Analytics (GA4): OAuth2 with both analytics.readonly and
analytics.manage.users.readonly (readonly alone 403s on the accounts
list); v1alpha accessBindings enumerated at account and property level
and merged by email; manual account picker (Pattern 1) with a
per-connection probe and name resolver; distinct from Google Workspace.
- Dotfile: API key in the X-DOTFILE-API-KEY header (Pattern 3); GET
/v1/users (owner/admin, suspended_at) with a static probe.
- Segment (Twilio): Public API token as Bearer with a required Region
setting (US or EU) mapped to the regional host; GET /users plus per-user
GET /users/{id} for roles and /invites for pending members; per-connection
BuildProbeURL.
- Square: OAuth2 (EMPLOYEES_READ) or a personal access token (Pattern 3);
POST /v2/team-members/search returns email/status/is_owner directly, so no
role resolution; custom probe and name resolver.
Google Analytics and Square are confidential OAuth clients, wired into the
bootstrap OAuth provider list and .env.example. Segment carries a required
extra setting, so the console add-source dialog maps region onto its
segmentRegion API-key input; without that mapping the value is silently
dropped and the create is rejected.
Cassette-backed driver tests plus unit tests for the Segment probe URL and
the bootstrap OAuth provider list.
Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
The access review Add Source dialog listed each connector with no
path to its setup documentation. Connectors that have a published
docs page on probo.com now surface a "Documentation" link on the
card, opening the page in a new tab; connectors without a page show
nothing extra.
The link is data-driven from the connector registry: a new
DocumentationURL on the provider Registration, populated for the 12
documented providers via a single accessReviewDocsURL helper, is
surfaced as a nullable documentationUrl on ConnectorProviderInfo and
rendered by the console only when present. This keeps the registry
the single source of truth and adds no client-side provider map.
The links resolve once the probo.com access-review docs pages are
deployed; until then they 404, so deploy the docs alongside this
change.
Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
Crisp is a managed (Model B) connector: Probo holds one plugin token
server-side and each connection carries only a Website ID. Nothing
stops one organization from entering another organization's Website
ID, so prove control of the website before creating the connection.
Probo derives a per-(organization, website) verification code as an
HMAC over the token secret and exposes it through a new
crispVerificationCode query. The customer pastes it into the Probo
plugin's per-website settings; at connect time the resolver reads the
setting back through the managed plugin token and requires a
constant-time match before any row is written. The managed key and
plugin ID come from bootstrap, so the connector stays hidden until the
deployment configures them.
The settings fetch is injected so the create-time gate's branch wiring
is unit-tested (mismatch and not-subscribed reject, internal errors
stay generic, a matching code passes), and the managed-versus-client
key resolution is covered too.
Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
Four API-key, single-tenant (Pattern 3) connectors:
- Scaleway: secret key in the X-Auth-Token header plus an Organization ID
setting; GET /iam/v1alpha1/users (owner/member, status, two-factor),
per-connection BuildProbeURL.
- Yousign: Bearer API key; GET /v3/users (admin/owner/member, is_active);
production host with a static probe.
- Railway: Bearer account token; GraphQL me{workspaces{members}} aggregated
and deduplicated across workspaces; custom probe, since Railway returns
HTTP 200 with an errors body on a rejected token.
- Crisp: plugin token as HTTP Basic (identifier:key) plus a Website ID
setting and the X-Crisp-Tier header; GET /v1/website/{id}/operators/list,
custom probe and name resolver.
Scaleway and Crisp carry a required extra setting, so the console add-source
dialog maps organizationId/websiteId onto their scalewayOrganizationId and
crispWebsiteId API-key inputs; without that mapping the value is silently
dropped and the create is rejected.
Cassette-backed driver tests plus unit tests for the cross-workspace
deduplication, the probe contracts and the role/MFA helpers.
Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
Add Pylon, OpenRouter, incident.io and Brevo as access-review connectors.
All are API-key, single-tenant providers (Pattern 3): the key identifies
one tenant, so there is no OAuth flow, picker UI, or bootstrap/helm
configuration.
- Pylon: Bearer token, GET /users; resolves each user's opaque role_id to
a role name via GET /user-roles, with cursor pagination.
- OpenRouter: Bearer management key, GET /api/v1/organization/members. The
endpoint requires an organization account -- a personal key authenticates
but returns 404 -- so the connection probe rejects 404 on top of 401/403
(doProbeRequest gained an opt-in extra-reject set) to surface a non-org
key at connect time instead of mid-campaign.
- incident.io: Bearer token, GET /v2/users. Its OAuth is outbound-only, so
the API key is the inbound path; live base_role/custom_roles take
precedence over the deprecated role enum.
- Brevo: API key in the api-key header (Registration.APIKeyHeader), GET
/v3/organization/invited/users. A live recording corrected the documented
schema: is_owner is a JSON boolean (not a string) and an id field is
present, so it is used as the stable ExternalID.
The OpenRouter and Brevo cassettes are anonymized live recordings; Pylon
and incident.io use hand-authored fixtures (no self-serve test tenant). The
shared three-valued active-status mapping is consolidated into
activeFromStatus in driver.go.
Each adds the enum value, migration, GraphQL binding, provider
Registration, a driver with a cassette-driven test, and a brand logo.
Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
Add Mercury, Apollo.io, Deepgram, ClickHouse Cloud, and Langfuse as
access-review connectors. All are API-key, single-tenant providers
(Pattern 3): the key identifies one tenant, so there is no OAuth flow,
picker UI, or bootstrap/helm configuration.
- Mercury: Bearer token, GET /api/v1/users, cursor pagination.
- Apollo.io: x-api-key header, GET /api/v1/users/search (teammates).
- Deepgram: Token scheme; lists members across every project and
dedupes by member_id, unioning per-project scopes.
- ClickHouse Cloud: HTTP Basic (keyId:keySecret); discovers the org
via GET /v1/organizations, then lists its members.
- Langfuse: HTTP Basic (publicKey:secretKey); a base-URL setting
selects the regional cloud host or a self-hosted instance.
Each adds the enum value, migration, GraphQL binding, provider
Registration, a driver with a cassette-driven test, and a brand logo.
Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
Register Neon as a connector provider and add a new access review
driver that fetches organization members from the Neon API with
cursor-based pagination.
Neon's OAuth is partner-gated, so the connector is API-key only
(Bearer, the default scheme). A personal or organization API key can
belong to several organizations; the operator supplies the ID of the
one to review. The members endpoint exposes per-user MFA state
(has_mfa) and deactivation, which map to the access entry MFA status
and active flag; the stable account UUID (user_id) is used as the
external ID over the membership ID.
Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
Register Render as an API-key connector provider and add an access
review driver that fetches workspace members from the Render API
(GET /v1/owners/{ownerId}/members).
Render exposes no partner OAuth program, so the connector authenticates
with a read-scoped API key (Authorization: Bearer) plus the customer's
Workspace ID. The flat members endpoint reports an explicit account
status and MFA flag, surfaced as the Active and MFAStatus fields; the
stable "usr-" id becomes ExternalID. There is no picker -- the
workspace is captured up front via ExtraSettings -- so
SetOrganizationSettings is omitted.
Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
Register Qovery as a connector provider and add a new access review
driver that fetches organization members from the Qovery API.
Extend API key connection handling with a configurable Authorization
token scheme so Qovery can use "Token" while existing providers
continue to default to Bearer.
Signed-off-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
Better Stack exposes team members and pending invitations through its
Uptime API. Wire it as an access-review connector so a Better Stack
team can be reviewed in access-review campaigns.
Better Stack has no third-party OAuth app for listing members (its
OAuth is an end-user MCP sign-in), so the connector authenticates with
a Bearer API token plus the team name that scopes the team-members
listing. The driver paginates /api/v2/team-members, maps roles and
invitation records into account records, and the source name is
resolved from the configured team.
This wires the full surface: the provider enum and migration, the
connector settings, the registry registration with the team-name extra
setting, the GraphQL input and resolver marshaling, the frontend field
mapping and connector logo, and cassette-backed driver tests.
Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
Add a SigNoz connector so its organization members can be pulled into
access-review campaign snapshots. SigNoz authenticates with a
SIGNOZ-API-KEY admin service-account key and a customer-supplied base
URL (a SigNoz Cloud region/tenant host or a self-hosted instance).
The driver lists users via GET /api/v1/user, which returns the role
(ADMIN/EDITOR/VIEWER) inline so admin detection works in a single call,
and maps the SigNoz user status (active / pending_invite / deleted) to
the account active flag. The name resolver reads the organization
display name from GET /api/v2/orgs/me to title the access source.
Wire the provider through the coredata enum and settings, the
connector-provider registry (driver and name-resolver factories), the
console API-key input schema and validation, the access-review source
label, and the SigNoz brand logo.
Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
Zendesk is a multi-tenant OAuth connector keyed by the customer
subdomain. The customer enters it at connect time; it rides the signed
state to the callback, is re-validated, and is stored on the connector
settings to build the API host.
List staff (agents and admins) via GET /api/v2/users.json with cursor
pagination, mapping role, active/suspended, and 2FA status; end-users
are excluded. The subdomain is validated as a single DNS label at every
trust boundary to close the SSRF vector, and the data client keeps the
SSRF-protected transport.
Zendesk OAuth across customer subdomains requires a Zendesk-approved
global OAuth client; the connector goes live once those credentials are
supplied via bootstrap.
Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
Introduce the OKTA value to the connector_provider enum (Go const,
ConnectorProviders slice, IsValid switch, GraphQL @goEnum, and the
Postgres ALTER TYPE migration) and an OktaConnectorSettings struct
holding the customer's Okta org domain.
Okta is a per-tenant IdP with no central API gateway, so the
connector is keyed on the org domain rather than a shared host. The
oktaDomain field on CreateAPIKeyConnectorInput lets the API-key flow
carry it.
Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
Wire Clerk in as a supported connector provider for access\nreviews and expose it through the console GraphQL provider enum.\n\nAdd a dedicated Clerk driver that lists users from the Clerk\nBackend API, maps account state and authentication signals into\nAccountRecord fields, and covers the behavior with focused driver\nand provider tests.\n\nInclude a migration that appends CLERK to the connector_provider\nenum so environments can persist Clerk connectors safely.
Signed-off-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
Signed-off-by: Cursor Agent <cursoragent@cursor.com>
Implement a SendGrid access-review driver that fetches teammates
from the SendGrid API and maps them into AccountRecord values.
Register SendGrid as a connector provider, expose it through the
connector provider enum, and add a migration that extends the
connector_provider type with SENDGRID.
Cover the new driver with a VCR-backed fixture test and helper
tests for role and response-shape handling to keep parsing robust.
Signed-off-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
Fold POSTHOG_SELF_HOSTED into POSTHOG: one provider now covers Cloud (OAuth + region-pinned API key) and self-hosted (API key + instance URL), since both already share the driver, name resolver, and PostHogConnectorSettings{BaseURL}. The API-key form picks a deployment (Cloud US/EU or self-hosted URL); the resolver requires exactly one of region/instanceUrl.
Drop the POSTHOG_SELF_HOSTED enum value, registration, migration, and logo mapping. Extract the deployment selector into a dedicated PostHogDeploymentField component. Point the driver tests at us.posthog.com instead of the legacy app.posthog.com host.
Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
Marshal the region (us/eu) for PostHog Cloud and the instance URL
for PostHog Self-Hosted from the create-connector input, validating
each, and add the matching GraphQL inputs and the POSTHOG_SELF_HOSTED
enum value.
Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
Introduce a PostHog access-review driver that lists organization\nmembers and maps role, MFA, and timestamp fields into account\nrecords.\n\nRegister PostHog as a builtin API-key connector provider and expose\nit through the connector provider enum so access-review source\ncreation can discover it.
Signed-off-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
Implement Metabase as a first-class access review connector backed by
GET /api/user, including account mapping and error handling in the
driver. Register the provider with API-key auth metadata and required
instance URL settings so connectors can be created and resolved
consistently.
Expose Metabase through the console GraphQL and UI flows by adding the
provider enum value, API-key extra setting field wiring, and source
label mapping. Add migration support for the connector_provider enum and
cover driver/provider behavior with focused tests.
Signed-off-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
Signed-off-by: Cursor Agent <cursoragent@cursor.com>
Add Grafana as an access review connector-backed source.
This introduces a Grafana access-review driver, provider registration,
and connector settings for the Grafana base URL. It also wires the
new provider through GraphQL and access-review UI input mapping so
API-key connectors can be created from the product.
A connector_provider enum migration is included so Grafana can be
persisted in existing databases.
Signed-off-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
Wire Cursor into the connector-provider registry as an API-key,
single-tenant connector using HTTP Basic auth, with no picker,
settings, or name resolver. Add the CURSOR enum value, its
migration, and the GraphQL enum binding so the provider is
accepted across the API surface.
Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
Add the ANTHROPIC connector_provider enum value, its migration, and the
GraphQL enum binding, then register the provider as an API-key connector
that authenticates via x-api-key.
The probe URL is left empty because the shared connection probe cannot
send the required anthropic-version header and would misreport a valid
key; a dead key surfaces on the first member fetch instead.
Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
- Require CLIENT_SECRET for new access-review connectors
- Use Heroku account UUID as ExternalID
- Bump GitHub orgs picker to per_page=100
- Drop Snyk, Ramp, Lever, Deel access-review providers
Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
Microsoft 365's native SCIM endpoint is unreliable, so mirror the
Google Workspace bridge over Microsoft Graph: a new MICROSOFT_365
OAuth2 connector, a SCIM bridge provider listing /v1.0/users with
$select pagination, and an access review driver that derives admin
status from /directoryRoles members. Refactor the bridge runner to
share OAuth2 plumbing across providers and surface the new bridge
type, scopes, UI card, and bootstrap env wiring.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
Move Organization, Identity, TrustCenter, and Viewer definitions to
include all their connection fields directly, removing all extend type
blocks for these hub types from entity files.
This eliminates the Relay schemaExtensions constraint where extend type
could only target types defined in the main schema file. Entity files
now only define their own standalone types and extend type Mutation.
Signed-off-by: Émile Ré <emile@getprobo.com>