Add Zendesk access-review connector

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>
This commit is contained in:
Aurélien Sibiril
2026-06-04 18:32:55 +02:00
parent 1c40121591
commit dbd920dc24
19 changed files with 891 additions and 14 deletions

View File

@@ -951,6 +951,23 @@ func (r *oktaNameResolver) ResolveInstanceName(ctx context.Context) (string, err
return resp.Subdomain, nil
}
// zendeskNameResolver returns the Zendesk subdomain stored in connector
// settings (e.g. "acme" for acme.zendesk.com), captured at connect time. No
// HTTP call is required; the AccessSource title becomes "Zendesk <subdomain>".
// Account-name resolution is intentionally omitted to keep the scope to
// users:read (Zendesk exposes no human account name on that scope).
type zendeskNameResolver struct {
subdomain string
}
func NewZendeskNameResolver(subdomain string) NameResolver {
return &zendeskNameResolver{subdomain: subdomain}
}
func (r *zendeskNameResolver) ResolveInstanceName(_ context.Context) (string, error) {
return r.subdomain, nil
}
// asanaNameResolver resolves the Asana workspace name.
type asanaNameResolver struct {
httpClient *http.Client