Add Neon access review driver support
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>
This commit is contained in:
@@ -408,6 +408,60 @@ func (r *renderNameResolver) ResolveInstanceName(ctx context.Context) (string, e
|
||||
return resp.Name, nil
|
||||
}
|
||||
|
||||
// neonNameResolver resolves the Neon organization name.
|
||||
type neonNameResolver struct {
|
||||
httpClient *http.Client
|
||||
organizationID string
|
||||
}
|
||||
|
||||
func NewNeonNameResolver(httpClient *http.Client, organizationID string) NameResolver {
|
||||
return &neonNameResolver{
|
||||
httpClient: httpClient,
|
||||
organizationID: organizationID,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *neonNameResolver) ResolveInstanceName(ctx context.Context) (string, error) {
|
||||
if r.organizationID == "" {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
endpoint, err := url.JoinPath(neonAPIBaseURL, "organizations", url.PathEscape(r.organizationID))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("cannot build neon organization URL: %w", err)
|
||||
}
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("cannot create neon organization request: %w", err)
|
||||
}
|
||||
|
||||
req.Header.Set("Accept", "application/json")
|
||||
|
||||
httpResp, err := r.httpClient.Do(req)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("cannot execute neon organization request: %w", err)
|
||||
}
|
||||
|
||||
defer func() { _ = httpResp.Body.Close() }()
|
||||
|
||||
// Best-effort: a non-2xx (revoked key, deleted org, stale ID) must not
|
||||
// make the source-name worker retry forever. Give up gracefully and keep
|
||||
// the generic source name; a dead key surfaces on the next ListAccounts.
|
||||
if httpResp.StatusCode < 200 || httpResp.StatusCode >= 300 {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
var resp struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
if err := json.NewDecoder(httpResp.Body).Decode(&resp); err != nil {
|
||||
return "", fmt.Errorf("cannot decode neon organization response: %w", err)
|
||||
}
|
||||
|
||||
return resp.Name, nil
|
||||
}
|
||||
|
||||
// hubspotNameResolver resolves the HubSpot account name.
|
||||
type hubspotNameResolver struct {
|
||||
httpClient *http.Client
|
||||
|
||||
Reference in New Issue
Block a user