diff --git a/.env.example b/.env.example index 9aab20142..f2235f2f6 100644 --- a/.env.example +++ b/.env.example @@ -108,12 +108,8 @@ # CONNECTOR_PAGERDUTY_CLIENT_SECRET= # CONNECTOR_ASANA_CLIENT_ID= # CONNECTOR_ASANA_CLIENT_SECRET= -# CONNECTOR_SNYK_CLIENT_ID= -# CONNECTOR_SNYK_CLIENT_SECRET= # CONNECTOR_NETLIFY_CLIENT_ID= # CONNECTOR_NETLIFY_CLIENT_SECRET= -# CONNECTOR_RAMP_CLIENT_ID= -# CONNECTOR_RAMP_CLIENT_SECRET= # CONNECTOR_CLICKUP_CLIENT_ID= # CONNECTOR_CLICKUP_CLIENT_SECRET= # CONNECTOR_VERCEL_CLIENT_ID= @@ -121,10 +117,6 @@ # CONNECTOR_VERCEL_INTEGRATION_SLUG= # CONNECTOR_MONDAY_CLIENT_ID= # CONNECTOR_MONDAY_CLIENT_SECRET= -# CONNECTOR_LEVER_CLIENT_ID= -# CONNECTOR_LEVER_CLIENT_SECRET= -# CONNECTOR_DEEL_CLIENT_ID= -# CONNECTOR_DEEL_CLIENT_SECRET= # ── Custom domains (Pebble ACME via compose) ────────────────────────── # CUSTOM_DOMAINS_CNAME_TARGET=custom.getprobo.com diff --git a/contrib/helm/charts/probo/templates/deployment.yaml b/contrib/helm/charts/probo/templates/deployment.yaml index e3ff407ce..ba07344f2 100644 --- a/contrib/helm/charts/probo/templates/deployment.yaml +++ b/contrib/helm/charts/probo/templates/deployment.yaml @@ -283,7 +283,7 @@ spec: {{- end }} # Connectors (access-review OAuth2 providers added 2026) {{- range .Values.probo.connectors }} - {{- $accessReviewProviders := list "gitlab" "bitbucket" "heroku" "pagerduty" "asana" "snyk" "netlify" "ramp" "clickup" "vercel" "monday" "lever" "deel" }} + {{- $accessReviewProviders := list "gitlab" "bitbucket" "heroku" "pagerduty" "asana" "netlify" "clickup" "vercel" "monday" }} {{- if has .name $accessReviewProviders }} {{- $envPrefix := printf "CONNECTOR_%s" (.name | upper) }} {{- $secretPrefix := printf "connector-%s" .name }} diff --git a/contrib/helm/charts/probo/templates/secret.yaml b/contrib/helm/charts/probo/templates/secret.yaml index 20d227761..0c8cc4f3d 100644 --- a/contrib/helm/charts/probo/templates/secret.yaml +++ b/contrib/helm/charts/probo/templates/secret.yaml @@ -56,7 +56,7 @@ stringData: connector-slack-signing-secret: {{ .config.signingSecret | quote }} {{- end }} {{- end }} - {{- $accessReviewProviders := list "gitlab" "bitbucket" "heroku" "pagerduty" "asana" "snyk" "netlify" "ramp" "clickup" "vercel" "monday" "lever" "deel" }} + {{- $accessReviewProviders := list "gitlab" "bitbucket" "heroku" "pagerduty" "asana" "netlify" "clickup" "vercel" "monday" }} {{- if has .name $accessReviewProviders }} {{- if .config.clientId }} connector-{{ .name }}-client-id: {{ .config.clientId | quote }} diff --git a/contrib/helm/charts/probo/values-production.yaml.example b/contrib/helm/charts/probo/values-production.yaml.example index 45cbabd2d..61ce63527 100644 --- a/contrib/helm/charts/probo/values-production.yaml.example +++ b/contrib/helm/charts/probo/values-production.yaml.example @@ -185,7 +185,7 @@ probo: # Supported providers (set name to one of): # slack, hubspot, docusign, notion, github, sentry, intercom, brex, # google_workspace, gitlab, bitbucket, heroku, pagerduty, asana, - # snyk, netlify, ramp, clickup, vercel, monday, lever, deel + # netlify, clickup, vercel, monday # # Vercel additionally requires "integration-slug" so the templated # AuthURL ("https://vercel.com/integrations/{integration_slug}/new") diff --git a/e2e/console/access_review_test.go b/e2e/console/access_review_test.go index 54365e51f..f66bf72df 100644 --- a/e2e/console/access_review_test.go +++ b/e2e/console/access_review_test.go @@ -27,13 +27,12 @@ import ( const testCsvData = "email,full_name,role,job_title,is_admin,mfa_status,auth_method,last_login,account_created_at,external_id\njane@example.com,Jane Smith,admin,CTO,true,ENABLED,SSO,2026-01-15T00:00:00Z,2024-06-01T00:00:00Z,ext-jane" // TODO(access-review-connectors): add e2e tests for the new GitLab, -// Bitbucket, Heroku, PagerDuty, Asana, Snyk, Netlify, Ramp, ClickUp, -// Vercel, Monday.com, Lever, and Deel access-source connectors once the -// e2e factory grows OAuth2-connector seed support for arbitrary -// providers. For now the per-driver VCR tests under -// pkg/accessreview/drivers cover the fetch path; this e2e suite still -// exercises CSV + Slack which were the providers wired before these -// sprints. +// Bitbucket, Heroku, PagerDuty, Asana, Netlify, ClickUp, Vercel, and +// Monday.com access-source connectors once the e2e factory grows +// OAuth2-connector seed support for arbitrary providers. For now the +// per-driver VCR tests under pkg/accessreview/drivers cover the fetch +// path; this e2e suite still exercises CSV + Slack which were the +// providers wired before these sprints. func TestAccessSource_Create(t *testing.T) { t.Parallel() owner := testutil.NewClient(t, testutil.RoleOwner) diff --git a/packages/ui/src/Atoms/ThirdParties/Deel.tsx b/packages/ui/src/Atoms/ThirdParties/Deel.tsx deleted file mode 100644 index 2dc9b2e42..000000000 --- a/packages/ui/src/Atoms/ThirdParties/Deel.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import type { ComponentProps } from "react"; - -export function Deel(props: ComponentProps<"svg">) { - return ( - - - - ); -} diff --git a/packages/ui/src/Atoms/ThirdParties/Lever.tsx b/packages/ui/src/Atoms/ThirdParties/Lever.tsx deleted file mode 100644 index 9cfb6f929..000000000 --- a/packages/ui/src/Atoms/ThirdParties/Lever.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import type { ComponentProps } from "react"; - -export function Lever(props: ComponentProps<"svg">) { - return ( - - - - ); -} diff --git a/packages/ui/src/Atoms/ThirdParties/Ramp.tsx b/packages/ui/src/Atoms/ThirdParties/Ramp.tsx deleted file mode 100644 index deede6ff9..000000000 --- a/packages/ui/src/Atoms/ThirdParties/Ramp.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import type { ComponentProps } from "react"; - -export function Ramp(props: ComponentProps<"svg">) { - return ( - - - - ); -} diff --git a/packages/ui/src/Atoms/ThirdParties/Snyk.tsx b/packages/ui/src/Atoms/ThirdParties/Snyk.tsx deleted file mode 100644 index 83b422a49..000000000 --- a/packages/ui/src/Atoms/ThirdParties/Snyk.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import type { ComponentProps } from "react"; - -export function Snyk(props: ComponentProps<"svg">) { - return ( - - - - ); -} diff --git a/packages/ui/src/Atoms/ThirdParties/ThirdPartyLogo.tsx b/packages/ui/src/Atoms/ThirdParties/ThirdPartyLogo.tsx index 8f2550d93..0e6f2bf1a 100644 --- a/packages/ui/src/Atoms/ThirdParties/ThirdPartyLogo.tsx +++ b/packages/ui/src/Atoms/ThirdParties/ThirdPartyLogo.tsx @@ -19,7 +19,6 @@ import { Bitbucket } from "./Bitbucket"; import { Brex } from "./Brex"; import { ClickUp } from "./ClickUp"; import { Cloudflare } from "./Cloudflare"; -import { Deel } from "./Deel"; import { DocuSign } from "./DocuSign"; import { Figma } from "./Figma"; import { GitHub } from "./GitHub"; @@ -28,7 +27,6 @@ import { Google } from "./Google"; import { Heroku } from "./Heroku"; import { HubSpot } from "./HubSpot"; import { Intercom } from "./Intercom"; -import { Lever } from "./Lever"; import { Linear } from "./Linear"; import { Microsoft } from "./Microsoft"; import { Monday } from "./Monday"; @@ -37,11 +35,9 @@ import { Notion } from "./Notion"; import { OnePassword } from "./OnePassword"; import { OpenAI } from "./OpenAI"; import { PagerDuty } from "./PagerDuty"; -import { Ramp } from "./Ramp"; import { Resend } from "./Resend"; import { Sentry } from "./Sentry"; import { Slack } from "./Slack"; -import { Snyk } from "./Snyk"; import { Supabase } from "./Supabase"; import { Tally } from "./Tally"; import { Vercel } from "./Vercel"; @@ -52,7 +48,6 @@ const thirdParties: Record>> = { BREX: Brex, CLICKUP: ClickUp, CLOUDFLARE: Cloudflare, - DEEL: Deel, DOCUSIGN: DocuSign, FIGMA: Figma, GITHUB: GitHub, @@ -62,7 +57,6 @@ const thirdParties: Record>> = { HEROKU: Heroku, HUBSPOT: HubSpot, INTERCOM: Intercom, - LEVER: Lever, LINEAR: Linear, MICROSOFT: Microsoft, MICROSOFT_365: Microsoft, @@ -73,11 +67,9 @@ const thirdParties: Record>> = { ONEPASSWORD: OnePassword, OPENAI: OpenAI, PAGERDUTY: PagerDuty, - RAMP: Ramp, RESEND: Resend, SENTRY: Sentry, SLACK: Slack, - SNYK: Snyk, SUPABASE: Supabase, TALLY: Tally, VERCEL: Vercel, diff --git a/packages/ui/src/Atoms/ThirdParties/index.ts b/packages/ui/src/Atoms/ThirdParties/index.ts index 61c9ede04..b10f4f840 100644 --- a/packages/ui/src/Atoms/ThirdParties/index.ts +++ b/packages/ui/src/Atoms/ThirdParties/index.ts @@ -3,7 +3,6 @@ export { Bitbucket } from "./Bitbucket"; export { Brex } from "./Brex"; export { ClickUp } from "./ClickUp"; export { Cloudflare } from "./Cloudflare"; -export { Deel } from "./Deel"; export { DocuSign } from "./DocuSign"; export { Figma } from "./Figma"; export { GitHub } from "./GitHub"; @@ -12,7 +11,6 @@ export { Google } from "./Google"; export { Heroku } from "./Heroku"; export { HubSpot } from "./HubSpot"; export { Intercom } from "./Intercom"; -export { Lever } from "./Lever"; export { Linear } from "./Linear"; export { Microsoft } from "./Microsoft"; export { Monday } from "./Monday"; @@ -21,11 +19,9 @@ export { Notion } from "./Notion"; export { OnePassword } from "./OnePassword"; export { OpenAI } from "./OpenAI"; export { PagerDuty } from "./PagerDuty"; -export { Ramp } from "./Ramp"; export { Resend } from "./Resend"; export { Sentry } from "./Sentry"; export { Slack } from "./Slack"; -export { Snyk } from "./Snyk"; export { Supabase } from "./Supabase"; export { Tally } from "./Tally"; export { ThirdPartyLogo } from "./ThirdPartyLogo"; diff --git a/pkg/accessreview/access_source_service.go b/pkg/accessreview/access_source_service.go index b81c22471..75d0656d3 100644 --- a/pkg/accessreview/access_source_service.go +++ b/pkg/accessreview/access_source_service.go @@ -412,12 +412,6 @@ func (s AccessSourceService) ConfigureAccessSource( }); err != nil { return fmt.Errorf("cannot set asana settings: %w", err) } - case coredata.ConnectorProviderSnyk: - if err := dbConnector.SetSettings(&coredata.SnykConnectorSettings{ - OrgID: req.OrganizationSlug, - }); err != nil { - return fmt.Errorf("cannot set snyk settings: %w", err) - } case coredata.ConnectorProviderNetlify: if err := dbConnector.SetSettings(&coredata.NetlifyConnectorSettings{ AccountSlug: req.OrganizationSlug, diff --git a/pkg/accessreview/drivers/deel.go b/pkg/accessreview/drivers/deel.go deleted file mode 100644 index ab63943eb..000000000 --- a/pkg/accessreview/drivers/deel.go +++ /dev/null @@ -1,136 +0,0 @@ -// Copyright (c) 2026 Probo Inc . -// -// Permission to use, copy, modify, and/or distribute this software for any -// purpose with or without fee is hereby granted, provided that the above -// copyright notice and this permission notice appear in all copies. -// -// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH -// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, -// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM -// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR -// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR -// PERFORMANCE OF THIS SOFTWARE. - -package drivers - -import ( - "context" - "encoding/json" - "fmt" - "net/http" - "net/url" - "strconv" - "strings" - - "go.probo.inc/probo/pkg/coredata" -) - -// DeelDriver fetches people from the Deel REST API using a -// pre-authenticated HTTP client (Bearer token). Pagination is -// offset-based: increment `offset` by `limit` until the response -// `data` array is empty. -// -// Notes on data quality: -// - Active is derived from `hiring_status == "active"`. When `end_date` -// is set the worker is considered inactive regardless of hiring_status. -// - MFA and last-login are not exposed by the people endpoint. -type DeelDriver struct { - httpClient *http.Client -} - -var _ Driver = (*DeelDriver)(nil) - -func NewDeelDriver(httpClient *http.Client) *DeelDriver { - return &DeelDriver{httpClient: httpClient} -} - -type deelPerson struct { - ID string `json:"id"` - Email string `json:"email"` - FirstName string `json:"first_name"` - LastName string `json:"last_name"` - JobTitle string `json:"job_title"` - HiringStatus string `json:"hiring_status"` - StartDate string `json:"start_date"` - EndDate string `json:"end_date"` -} - -type deelPeoplePage struct { - Data []deelPerson `json:"data"` -} - -func (d *DeelDriver) ListAccounts(ctx context.Context) ([]AccountRecord, error) { - var records []AccountRecord - - const limit = 100 - offset := 0 - - for range maxPaginationPages { - people, err := d.queryPeople(ctx, offset, limit) - if err != nil { - return nil, err - } - - if len(people) == 0 { - return records, nil - } - - for _, p := range people { - fullName := strings.TrimSpace(p.FirstName + " " + p.LastName) - - active := p.HiringStatus == "active" - if p.EndDate != "" { - active = false - } - - record := AccountRecord{ - Email: p.Email, - FullName: fullName, - JobTitle: p.JobTitle, - Active: &active, - MFAStatus: coredata.MFAStatusUnknown, - AuthMethod: coredata.AccessEntryAuthMethodUnknown, - AccountType: coredata.AccessEntryAccountTypeUser, - ExternalID: p.ID, - } - - records = append(records, record) - } - - offset += limit - } - - return nil, fmt.Errorf("cannot list all deel accounts: %w", ErrPaginationLimitReached) -} - -func (d *DeelDriver) queryPeople(ctx context.Context, offset, limit int) ([]deelPerson, error) { - q := url.Values{} - q.Set("limit", strconv.Itoa(limit)) - q.Set("offset", strconv.Itoa(offset)) - u := url.URL{Scheme: "https", Host: "api.letsdeel.com", Path: "/rest/v2/people", RawQuery: q.Encode()} - endpoint := u.String() - - req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil) - if err != nil { - return nil, fmt.Errorf("cannot create deel people request: %w", err) - } - req.Header.Set("Accept", "application/json") - - httpResp, err := d.httpClient.Do(req) - if err != nil { - return nil, fmt.Errorf("cannot execute deel people request: %w", err) - } - defer func() { _ = httpResp.Body.Close() }() - - if httpResp.StatusCode < 200 || httpResp.StatusCode >= 300 { - return nil, fmt.Errorf("cannot fetch deel people: unexpected status %d", httpResp.StatusCode) - } - - var page deelPeoplePage - if err := json.NewDecoder(httpResp.Body).Decode(&page); err != nil { - return nil, fmt.Errorf("cannot decode deel people response: %w", err) - } - - return page.Data, nil -} diff --git a/pkg/accessreview/drivers/deel_test.go b/pkg/accessreview/drivers/deel_test.go deleted file mode 100644 index 6a93f9155..000000000 --- a/pkg/accessreview/drivers/deel_test.go +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (c) 2026 Probo Inc . -// -// Permission to use, copy, modify, and/or distribute this software for any -// purpose with or without fee is hereby granted, provided that the above -// copyright notice and this permission notice appear in all copies. -// -// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH -// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, -// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM -// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR -// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR -// PERFORMANCE OF THIS SOFTWARE. - -package drivers - -import ( - "context" - "os" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestDeelDriver(t *testing.T) { - t.Parallel() - - rec := newRecorder(t, "testdata/deel", "DEEL_TOKEN") - client := newVCRClient(rec, bearerAuth(os.Getenv("DEEL_TOKEN"))) - - driver := NewDeelDriver(client) - records, err := driver.ListAccounts(context.Background()) - require.NoError(t, err) - require.NotEmpty(t, records) - - r := records[0] - assert.NotEmpty(t, r.Email) - assert.NotEmpty(t, r.ExternalID) - assert.NotEmpty(t, r.FullName) - assert.NotEmpty(t, r.JobTitle) - require.NotNil(t, r.Active) - assert.True(t, *r.Active) - - // Inactive (or end_date set) people surface as Active=false. - require.Len(t, records, 2) - require.NotNil(t, records[1].Active) - assert.False(t, *records[1].Active) -} diff --git a/pkg/accessreview/drivers/heroku.go b/pkg/accessreview/drivers/heroku.go index 44137adae..8c66a92aa 100644 --- a/pkg/accessreview/drivers/heroku.go +++ b/pkg/accessreview/drivers/heroku.go @@ -96,6 +96,11 @@ func (d *HerokuDriver) ListAccounts(ctx context.Context) ([]AccountRecord, error isAdmin := m.Role == "admin" || m.Role == "owner" + externalID := m.User.ID + if externalID == "" { + externalID = m.ID + } + record := AccountRecord{ Email: email, FullName: fullName, @@ -104,7 +109,7 @@ func (d *HerokuDriver) ListAccounts(ctx context.Context) ([]AccountRecord, error MFAStatus: mfaStatus, AuthMethod: coredata.AccessEntryAuthMethodUnknown, AccountType: coredata.AccessEntryAccountTypeUser, - ExternalID: email, + ExternalID: externalID, } if m.CreatedAt != "" { diff --git a/pkg/accessreview/drivers/lever.go b/pkg/accessreview/drivers/lever.go deleted file mode 100644 index 8445857ce..000000000 --- a/pkg/accessreview/drivers/lever.go +++ /dev/null @@ -1,149 +0,0 @@ -// Copyright (c) 2026 Probo Inc . -// -// Permission to use, copy, modify, and/or distribute this software for any -// purpose with or without fee is hereby granted, provided that the above -// copyright notice and this permission notice appear in all copies. -// -// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH -// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, -// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM -// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR -// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR -// PERFORMANCE OF THIS SOFTWARE. - -package drivers - -import ( - "context" - "encoding/json" - "fmt" - "net/http" - "net/url" - "time" - - "go.probo.inc/probo/pkg/coredata" -) - -// LeverDriver fetches users from the Lever REST API using a -// pre-authenticated HTTP client (Bearer token from the Auth0-backed -// flow). Pagination is body-cursor based: response carries `data[]`, -// `hasNext` (bool), and `next` (cursor). The next request appends -// `?offset=`. -// -// Notes on data quality: -// - `lastLoggedInAt` and `createdAt` are epoch milliseconds — -// defensive parse, may be null/undocumented. -// - MFA is exposed only via SCIM/SSO, not the REST API. -// - Active is derived from `deactivatedAt`: nil/missing = active. -type LeverDriver struct { - httpClient *http.Client -} - -var _ Driver = (*LeverDriver)(nil) - -func NewLeverDriver(httpClient *http.Client) *LeverDriver { - return &LeverDriver{ - httpClient: &http.Client{ - Transport: &retryRoundTripper{ - next: httpClient.Transport, - maxRetries: 3, - }, - }, - } -} - -type leverUser struct { - ID string `json:"id"` - Email string `json:"email"` - Name string `json:"name"` - AccessRole string `json:"accessRole"` - DeactivatedAt *int64 `json:"deactivatedAt"` - LastLoggedInAt *int64 `json:"lastLoggedInAt"` - CreatedAt *int64 `json:"createdAt"` -} - -type leverUsersPage struct { - Data []leverUser `json:"data"` - HasNext bool `json:"hasNext"` - Next string `json:"next"` -} - -func (d *LeverDriver) ListAccounts(ctx context.Context) ([]AccountRecord, error) { - var records []AccountRecord - - cursor := "" - for range maxPaginationPages { - page, err := d.queryUsers(ctx, cursor) - if err != nil { - return nil, err - } - - for _, u := range page.Data { - active := u.DeactivatedAt == nil - - record := AccountRecord{ - Email: u.Email, - FullName: u.Name, - Role: u.AccessRole, - Active: &active, - MFAStatus: coredata.MFAStatusUnknown, - AuthMethod: coredata.AccessEntryAuthMethodUnknown, - AccountType: coredata.AccessEntryAccountTypeUser, - ExternalID: u.ID, - } - - if u.LastLoggedInAt != nil { - t := time.UnixMilli(*u.LastLoggedInAt) - record.LastLogin = &t - } - - if u.CreatedAt != nil { - t := time.UnixMilli(*u.CreatedAt) - record.CreatedAt = &t - } - - records = append(records, record) - } - - if !page.HasNext || page.Next == "" { - return records, nil - } - cursor = page.Next - } - - return nil, fmt.Errorf("cannot list all lever accounts: %w", ErrPaginationLimitReached) -} - -func (d *LeverDriver) queryUsers(ctx context.Context, cursor string) (*leverUsersPage, error) { - q := url.Values{} - q.Set("limit", "100") - if cursor != "" { - q.Set("offset", cursor) - } - u := url.URL{Scheme: "https", Host: "api.lever.co", Path: "/v1/users", RawQuery: q.Encode()} - endpoint := u.String() - - req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil) - if err != nil { - return nil, fmt.Errorf("cannot create lever users request: %w", err) - } - req.Header.Set("Accept", "application/json") - - httpResp, err := d.httpClient.Do(req) - if err != nil { - return nil, fmt.Errorf("cannot execute lever users request: %w", err) - } - defer func() { _ = httpResp.Body.Close() }() - - if httpResp.StatusCode < 200 || httpResp.StatusCode >= 300 { - return nil, fmt.Errorf("cannot fetch lever users: unexpected status %d", httpResp.StatusCode) - } - - var page leverUsersPage - if err := json.NewDecoder(httpResp.Body).Decode(&page); err != nil { - return nil, fmt.Errorf("cannot decode lever users response: %w", err) - } - - return &page, nil -} diff --git a/pkg/accessreview/drivers/lever_test.go b/pkg/accessreview/drivers/lever_test.go deleted file mode 100644 index c6e8be45c..000000000 --- a/pkg/accessreview/drivers/lever_test.go +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright (c) 2026 Probo Inc . -// -// Permission to use, copy, modify, and/or distribute this software for any -// purpose with or without fee is hereby granted, provided that the above -// copyright notice and this permission notice appear in all copies. -// -// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH -// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, -// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM -// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR -// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR -// PERFORMANCE OF THIS SOFTWARE. - -package drivers - -import ( - "context" - "os" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestLeverDriver(t *testing.T) { - t.Parallel() - - rec := newRecorder(t, "testdata/lever", "LEVER_TOKEN") - client := newVCRClient(rec, bearerAuth(os.Getenv("LEVER_TOKEN"))) - - driver := NewLeverDriver(client) - records, err := driver.ListAccounts(context.Background()) - require.NoError(t, err) - require.NotEmpty(t, records) - - r := records[0] - assert.NotEmpty(t, r.Email) - assert.NotEmpty(t, r.ExternalID) - assert.NotEmpty(t, r.FullName) - assert.NotEmpty(t, r.Role) - require.NotNil(t, r.Active) - assert.True(t, *r.Active) - require.NotNil(t, r.LastLogin) - - // Deactivated users (deactivatedAt non-null) should surface as Active=false. - require.Len(t, records, 2) - require.NotNil(t, records[1].Active) - assert.False(t, *records[1].Active) -} diff --git a/pkg/accessreview/drivers/name_resolver.go b/pkg/accessreview/drivers/name_resolver.go index 9d7577003..0ee051284 100644 --- a/pkg/accessreview/drivers/name_resolver.go +++ b/pkg/accessreview/drivers/name_resolver.go @@ -58,14 +58,10 @@ var providerDisplayNames = map[coredata.ConnectorProvider]string{ coredata.ConnectorProviderHeroku: "Heroku", coredata.ConnectorProviderPagerDuty: "PagerDuty", coredata.ConnectorProviderAsana: "Asana", - coredata.ConnectorProviderSnyk: "Snyk", coredata.ConnectorProviderNetlify: "Netlify", - coredata.ConnectorProviderRamp: "Ramp", coredata.ConnectorProviderClickUp: "ClickUp", coredata.ConnectorProviderVercel: "Vercel", coredata.ConnectorProviderMonday: "Monday.com", - coredata.ConnectorProviderLever: "Lever", - coredata.ConnectorProviderDeel: "Deel", } // ProviderDisplayName returns the human-readable label for a connector provider. @@ -802,53 +798,6 @@ func (r *asanaNameResolver) ResolveInstanceName(ctx context.Context) (string, er return resp.Data.Name, nil } -// snykNameResolver resolves the Snyk organization name. -type snykNameResolver struct { - httpClient *http.Client - orgID string -} - -func NewSnykNameResolver(httpClient *http.Client, orgID string) NameResolver { - return &snykNameResolver{httpClient: httpClient, orgID: orgID} -} - -func (r *snykNameResolver) ResolveInstanceName(ctx context.Context) (string, error) { - if r.orgID == "" { - return "", nil - } - - endpoint := fmt.Sprintf("https://api.snyk.io/rest/orgs/%s?version=2024-10-15", url.PathEscape(r.orgID)) - - req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil) - if err != nil { - return "", fmt.Errorf("cannot create snyk org request: %w", err) - } - req.Header.Set("Accept", "application/vnd.api+json") - - httpResp, err := r.httpClient.Do(req) - if err != nil { - return "", fmt.Errorf("cannot execute snyk org request: %w", err) - } - defer func() { _ = httpResp.Body.Close() }() - - if httpResp.StatusCode < 200 || httpResp.StatusCode >= 300 { - return "", fmt.Errorf("cannot fetch snyk org: unexpected status %d", httpResp.StatusCode) - } - - var resp struct { - Data struct { - Attributes struct { - Name string `json:"name"` - } `json:"attributes"` - } `json:"data"` - } - if err := json.NewDecoder(httpResp.Body).Decode(&resp); err != nil { - return "", fmt.Errorf("cannot decode snyk org response: %w", err) - } - - return resp.Data.Attributes.Name, nil -} - // netlifyNameResolver resolves the Netlify account name. type netlifyNameResolver struct { httpClient *http.Client @@ -892,51 +841,6 @@ func (r *netlifyNameResolver) ResolveInstanceName(ctx context.Context) (string, return resp.Name, nil } -// rampNameResolver resolves the Ramp business name. -type rampNameResolver struct { - httpClient *http.Client -} - -func NewRampNameResolver(httpClient *http.Client) NameResolver { - return &rampNameResolver{httpClient: httpClient} -} - -func (r *rampNameResolver) ResolveInstanceName(ctx context.Context) (string, error) { - req, err := http.NewRequestWithContext( - ctx, - http.MethodGet, - "https://api.ramp.com/developer/v1/business", - nil, - ) - if err != nil { - return "", fmt.Errorf("cannot create ramp business request: %w", err) - } - req.Header.Set("Accept", "application/json") - - httpResp, err := r.httpClient.Do(req) - if err != nil { - return "", fmt.Errorf("cannot execute ramp business request: %w", err) - } - defer func() { _ = httpResp.Body.Close() }() - - if httpResp.StatusCode < 200 || httpResp.StatusCode >= 300 { - return "", fmt.Errorf("cannot fetch ramp business: unexpected status %d", httpResp.StatusCode) - } - - var resp struct { - BusinessName string `json:"business_name"` - LegalBusinessName string `json:"legal_business_name"` - } - if err := json.NewDecoder(httpResp.Body).Decode(&resp); err != nil { - return "", fmt.Errorf("cannot decode ramp business response: %w", err) - } - - if resp.BusinessName != "" { - return resp.BusinessName, nil - } - return resp.LegalBusinessName, nil -} - // clickupNameResolver resolves the ClickUp team name. type clickupNameResolver struct { httpClient *http.Client @@ -1102,67 +1006,6 @@ func (r *mondayNameResolver) ResolveInstanceName(ctx context.Context) (string, e return resp.Data.Account.Name, nil } -// leverNameResolver returns an empty string: Lever does not expose a -// dedicated org-name endpoint. The worker keeps the generic name and -// the operator can rename the source manually. -type leverNameResolver struct{} - -func NewLeverNameResolver() NameResolver { - return &leverNameResolver{} -} - -func (r *leverNameResolver) ResolveInstanceName(_ context.Context) (string, error) { - return "", nil -} - -// deelNameResolver resolves the Deel organization name by reading the -// first item of /rest/v2/organizations. -type deelNameResolver struct { - httpClient *http.Client -} - -func NewDeelNameResolver(httpClient *http.Client) NameResolver { - return &deelNameResolver{httpClient: httpClient} -} - -func (r *deelNameResolver) ResolveInstanceName(ctx context.Context) (string, error) { - req, err := http.NewRequestWithContext( - ctx, - http.MethodGet, - "https://api.letsdeel.com/rest/v2/organizations", - nil, - ) - if err != nil { - return "", fmt.Errorf("cannot create deel organizations request: %w", err) - } - req.Header.Set("Accept", "application/json") - - httpResp, err := r.httpClient.Do(req) - if err != nil { - return "", fmt.Errorf("cannot execute deel organizations request: %w", err) - } - defer func() { _ = httpResp.Body.Close() }() - - if httpResp.StatusCode < 200 || httpResp.StatusCode >= 300 { - return "", fmt.Errorf("cannot fetch deel organizations: unexpected status %d", httpResp.StatusCode) - } - - var resp struct { - Data []struct { - Name string `json:"name"` - } `json:"data"` - } - if err := json.NewDecoder(httpResp.Body).Decode(&resp); err != nil { - return "", fmt.Errorf("cannot decode deel organizations response: %w", err) - } - - if len(resp.Data) == 0 { - return "", nil - } - - return resp.Data[0].Name, nil -} - // notionNameResolver resolves the Notion workspace name via /v1/users/me. type notionNameResolver struct { httpClient *http.Client diff --git a/pkg/accessreview/drivers/oauth2_scopes.go b/pkg/accessreview/drivers/oauth2_scopes.go index e400a32f2..8d53f2fad 100644 --- a/pkg/accessreview/drivers/oauth2_scopes.go +++ b/pkg/accessreview/drivers/oauth2_scopes.go @@ -45,11 +45,7 @@ var providerOAuth2Scopes = map[coredata.ConnectorProvider][]string{ coredata.ConnectorProviderHeroku: {"read"}, coredata.ConnectorProviderPagerDuty: {"users.read"}, coredata.ConnectorProviderAsana: {"workspaces:read", "users:read"}, - coredata.ConnectorProviderSnyk: {"org.read", "org.membership.read", "offline_access"}, - coredata.ConnectorProviderRamp: {"users:read"}, coredata.ConnectorProviderMonday: {"users:read", "account:read"}, - coredata.ConnectorProviderLever: {"users:read:admin", "offline_access"}, - coredata.ConnectorProviderDeel: {"people:read", "organizations:read"}, // Notion and Intercom have no scopes here: Notion authorizes via // extra-auth-params (owner=user), Intercom configures scopes at the app // level. Bitbucket scopes are pinned on the OAuth consumer at diff --git a/pkg/accessreview/drivers/organizations.go b/pkg/accessreview/drivers/organizations.go index 29b0fdeca..233c7f78a 100644 --- a/pkg/accessreview/drivers/organizations.go +++ b/pkg/accessreview/drivers/organizations.go @@ -34,7 +34,7 @@ type Organization struct { // ListGitHubOrganizations fetches the organizations the authenticated // GitHub user belongs to. func ListGitHubOrganizations(ctx context.Context, httpClient *http.Client) ([]Organization, error) { - req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://api.github.com/user/orgs", nil) + req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://api.github.com/user/orgs?per_page=100", nil) if err != nil { return nil, fmt.Errorf("cannot create github organizations request: %w", err) } @@ -301,58 +301,6 @@ func ListAsanaOrganizations(ctx context.Context, httpClient *http.Client) ([]Org return result, nil } -// ListSnykOrganizations fetches the Snyk organizations the authenticated -// user belongs to. The Snyk REST API is JSON:API; the org id is the -// unique identifier surfaced as the slug. -func ListSnykOrganizations(ctx context.Context, httpClient *http.Client) ([]Organization, error) { - req, err := http.NewRequestWithContext( - ctx, - http.MethodGet, - "https://api.snyk.io/rest/orgs?version=2024-10-15&limit=100", - nil, - ) - if err != nil { - return nil, fmt.Errorf("cannot create snyk organizations request: %w", err) - } - req.Header.Set("Accept", "application/vnd.api+json") - - resp, err := httpClient.Do(req) - if err != nil { - return nil, fmt.Errorf("cannot fetch snyk organizations: %w", err) - } - defer func() { _ = resp.Body.Close() }() - - if resp.StatusCode != http.StatusOK { - return nil, fmt.Errorf("cannot fetch snyk organizations: unexpected status %d", resp.StatusCode) - } - - var body struct { - Data []struct { - ID string `json:"id"` - Attributes struct { - Slug string `json:"slug"` - Name string `json:"name"` - } `json:"attributes"` - } `json:"data"` - } - if err := json.NewDecoder(resp.Body).Decode(&body); err != nil { - return nil, fmt.Errorf("cannot decode snyk organizations response: %w", err) - } - - result := make([]Organization, len(body.Data)) - for i, org := range body.Data { - displayName := org.Attributes.Name - if displayName == "" { - displayName = org.Attributes.Slug - } - if displayName == "" { - displayName = org.ID - } - result[i] = Organization{Slug: org.ID, DisplayName: displayName} - } - return result, nil -} - // ListNetlifyOrganizations fetches the Netlify accounts the authenticated // user belongs to. func ListNetlifyOrganizations(ctx context.Context, httpClient *http.Client) ([]Organization, error) { diff --git a/pkg/accessreview/drivers/ramp.go b/pkg/accessreview/drivers/ramp.go deleted file mode 100644 index cec21ae74..000000000 --- a/pkg/accessreview/drivers/ramp.go +++ /dev/null @@ -1,137 +0,0 @@ -// Copyright (c) 2026 Probo Inc . -// -// Permission to use, copy, modify, and/or distribute this software for any -// purpose with or without fee is hereby granted, provided that the above -// copyright notice and this permission notice appear in all copies. -// -// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH -// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, -// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM -// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR -// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR -// PERFORMANCE OF THIS SOFTWARE. - -package drivers - -import ( - "context" - "encoding/json" - "fmt" - "net/http" - "strings" - "time" - - "go.probo.inc/probo/pkg/coredata" -) - -// RampDriver fetches users from the Ramp Developer API using a -// pre-authenticated HTTP client (Bearer token). Ramp grants are scoped -// to a single business — there is no per-business picker, so this is a -// Pattern 1 driver. Pagination is via the absolute URL exposed in -// `page.next` on the response body. -type RampDriver struct { - httpClient *http.Client -} - -var _ Driver = (*RampDriver)(nil) - -func NewRampDriver(httpClient *http.Client) *RampDriver { - return &RampDriver{ - httpClient: &http.Client{ - Transport: &retryRoundTripper{ - next: httpClient.Transport, - maxRetries: 3, - }, - }, - } -} - -type rampUser struct { - ID string `json:"id"` - Email string `json:"email"` - FirstName string `json:"first_name"` - LastName string `json:"last_name"` - Role string `json:"role"` - Status string `json:"status"` - LastLoginAt string `json:"last_login_at"` - IsManager bool `json:"is_manager"` -} - -type rampUsersPage struct { - Data []rampUser `json:"data"` - Page struct { - Next string `json:"next"` - } `json:"page"` -} - -func (d *RampDriver) ListAccounts(ctx context.Context) ([]AccountRecord, error) { - var records []AccountRecord - - next := "https://api.ramp.com/developer/v1/users?page_size=100" - - for range maxPaginationPages { - page, err := d.queryUsers(ctx, next) - if err != nil { - return nil, err - } - - for _, u := range page.Data { - fullName := strings.TrimSpace(u.FirstName + " " + u.LastName) - - active := u.Status == "USER_ACTIVE" - - record := AccountRecord{ - Email: u.Email, - FullName: fullName, - Role: u.Role, - Active: &active, - IsAdmin: u.IsManager, - ExternalID: u.ID, - MFAStatus: coredata.MFAStatusUnknown, - AuthMethod: coredata.AccessEntryAuthMethodUnknown, - AccountType: coredata.AccessEntryAccountTypeUser, - } - - if u.LastLoginAt != "" { - if t, err := time.Parse(time.RFC3339, u.LastLoginAt); err == nil { - record.LastLogin = &t - } - } - - records = append(records, record) - } - - if page.Page.Next == "" { - return records, nil - } - next = page.Page.Next - } - - return nil, fmt.Errorf("cannot list all ramp accounts: %w", ErrPaginationLimitReached) -} - -func (d *RampDriver) queryUsers(ctx context.Context, endpoint string) (*rampUsersPage, error) { - req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil) - if err != nil { - return nil, fmt.Errorf("cannot create ramp users request: %w", err) - } - req.Header.Set("Accept", "application/json") - - httpResp, err := d.httpClient.Do(req) - if err != nil { - return nil, fmt.Errorf("cannot execute ramp users request: %w", err) - } - defer func() { _ = httpResp.Body.Close() }() - - if httpResp.StatusCode < 200 || httpResp.StatusCode >= 300 { - return nil, fmt.Errorf("cannot fetch ramp users: unexpected status %d", httpResp.StatusCode) - } - - var page rampUsersPage - if err := json.NewDecoder(httpResp.Body).Decode(&page); err != nil { - return nil, fmt.Errorf("cannot decode ramp users response: %w", err) - } - - return &page, nil -} diff --git a/pkg/accessreview/drivers/ramp_test.go b/pkg/accessreview/drivers/ramp_test.go deleted file mode 100644 index d0f08d6cf..000000000 --- a/pkg/accessreview/drivers/ramp_test.go +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright (c) 2026 Probo Inc . -// -// Permission to use, copy, modify, and/or distribute this software for any -// purpose with or without fee is hereby granted, provided that the above -// copyright notice and this permission notice appear in all copies. -// -// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH -// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, -// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM -// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR -// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR -// PERFORMANCE OF THIS SOFTWARE. - -package drivers - -import ( - "context" - "os" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestRampDriver(t *testing.T) { - t.Parallel() - - rec := newRecorder(t, "testdata/ramp", "RAMP_TOKEN") - client := newVCRClient(rec, bearerAuth(os.Getenv("RAMP_TOKEN"))) - - driver := NewRampDriver(client) - records, err := driver.ListAccounts(context.Background()) - require.NoError(t, err) - require.Len(t, records, 2) - - r := records[0] - assert.Equal(t, "user-1", r.ExternalID) - assert.Equal(t, "jane@example.com", r.Email) - assert.Equal(t, "Jane Doe", r.FullName) - assert.Equal(t, "BUSINESS_ADMIN", r.Role) - require.NotNil(t, r.Active) - assert.True(t, *r.Active) - assert.True(t, r.IsAdmin) - require.NotNil(t, r.LastLogin) - - // Suspended record should be Active=false. - require.NotNil(t, records[1].Active) - assert.False(t, *records[1].Active) -} diff --git a/pkg/accessreview/drivers/snyk.go b/pkg/accessreview/drivers/snyk.go deleted file mode 100644 index 1d04dc35e..000000000 --- a/pkg/accessreview/drivers/snyk.go +++ /dev/null @@ -1,145 +0,0 @@ -// Copyright (c) 2026 Probo Inc . -// -// Permission to use, copy, modify, and/or distribute this software for any -// purpose with or without fee is hereby granted, provided that the above -// copyright notice and this permission notice appear in all copies. -// -// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH -// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, -// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM -// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR -// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR -// PERFORMANCE OF THIS SOFTWARE. - -package drivers - -import ( - "context" - "encoding/json" - "fmt" - "net/http" - "net/url" - "strings" - - "go.probo.inc/probo/pkg/coredata" -) - -// SnykDriver fetches organization memberships from the Snyk REST API -// using a pre-authenticated HTTP client (Bearer token from the Snyk -// Apps OAuth PKCE flow). Pagination is via the `links.next` field on -// the response body (a relative URL fragment under api.snyk.io). -// -// Note: Snyk uses a single-use rotating refresh token (~180d TTL). -// Persistence of the rotated refresh token is handled by the existing -// callers — see pkg/accessreview/access_source_service.go:336-347 for -// the campaign-fetch path and pkg/accessreview/source_name_worker.go:121-128 -// for the source-name path. Both run inside a transaction so concurrent -// runs serialise per-row. -type SnykDriver struct { - httpClient *http.Client - orgID string -} - -var _ Driver = (*SnykDriver)(nil) - -func NewSnykDriver(httpClient *http.Client, orgID string) *SnykDriver { - return &SnykDriver{ - httpClient: &http.Client{ - Transport: &retryRoundTripper{ - next: httpClient.Transport, - maxRetries: 3, - }, - }, - orgID: orgID, - } -} - -type snykMembership struct { - ID string `json:"id"` - Attributes struct { - User struct { - Email string `json:"email"` - Name string `json:"name"` - } `json:"user"` - Role struct { - Name string `json:"name"` - } `json:"role"` - } `json:"attributes"` -} - -type snykMembershipsPage struct { - Data []snykMembership `json:"data"` - Links struct { - Next string `json:"next"` - } `json:"links"` -} - -func (d *SnykDriver) ListAccounts(ctx context.Context) ([]AccountRecord, error) { - var records []AccountRecord - - next := fmt.Sprintf( - "https://api.snyk.io/rest/orgs/%s/memberships?version=2024-10-15&limit=100", - url.PathEscape(d.orgID), - ) - - for range maxPaginationPages { - page, err := d.queryMemberships(ctx, next) - if err != nil { - return nil, err - } - - for _, m := range page.Data { - record := AccountRecord{ - Email: m.Attributes.User.Email, - FullName: m.Attributes.User.Name, - Role: m.Attributes.Role.Name, - ExternalID: m.ID, - MFAStatus: coredata.MFAStatusUnknown, - AuthMethod: coredata.AccessEntryAuthMethodUnknown, - AccountType: coredata.AccessEntryAccountTypeUser, - } - records = append(records, record) - } - - if page.Links.Next == "" { - return records, nil - } - - // Snyk surfaces `links.next` as either a path-only fragment - // (e.g. "/rest/orgs//memberships?...&starting_after=...") - // or an absolute URL. Normalise to absolute. - if strings.HasPrefix(page.Links.Next, "http://") || strings.HasPrefix(page.Links.Next, "https://") { - next = page.Links.Next - } else { - next = "https://api.snyk.io" + page.Links.Next - } - } - - return nil, fmt.Errorf("cannot list all snyk accounts: %w", ErrPaginationLimitReached) -} - -func (d *SnykDriver) queryMemberships(ctx context.Context, endpoint string) (*snykMembershipsPage, error) { - req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil) - if err != nil { - return nil, fmt.Errorf("cannot create snyk memberships request: %w", err) - } - req.Header.Set("Accept", "application/vnd.api+json") - - httpResp, err := d.httpClient.Do(req) - if err != nil { - return nil, fmt.Errorf("cannot execute snyk memberships request: %w", err) - } - defer func() { _ = httpResp.Body.Close() }() - - if httpResp.StatusCode < 200 || httpResp.StatusCode >= 300 { - return nil, fmt.Errorf("cannot fetch snyk memberships: unexpected status %d", httpResp.StatusCode) - } - - var page snykMembershipsPage - if err := json.NewDecoder(httpResp.Body).Decode(&page); err != nil { - return nil, fmt.Errorf("cannot decode snyk memberships response: %w", err) - } - - return &page, nil -} diff --git a/pkg/accessreview/drivers/snyk_test.go b/pkg/accessreview/drivers/snyk_test.go deleted file mode 100644 index 93afd84b6..000000000 --- a/pkg/accessreview/drivers/snyk_test.go +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright (c) 2026 Probo Inc . -// -// Permission to use, copy, modify, and/or distribute this software for any -// purpose with or without fee is hereby granted, provided that the above -// copyright notice and this permission notice appear in all copies. -// -// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH -// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, -// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM -// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR -// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR -// PERFORMANCE OF THIS SOFTWARE. - -package drivers - -import ( - "context" - "os" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestSnykDriver(t *testing.T) { - t.Parallel() - - rec := newRecorder(t, "testdata/snyk", "SNYK_TOKEN") - client := newVCRClient(rec, bearerAuth(os.Getenv("SNYK_TOKEN"))) - - orgID := os.Getenv("SNYK_ORG_ID") - if orgID == "" { - orgID = "org-1234" - } - - driver := NewSnykDriver(client, orgID) - records, err := driver.ListAccounts(context.Background()) - require.NoError(t, err) - require.Len(t, records, 2) - - r := records[0] - assert.Equal(t, "membership-1", r.ExternalID) - assert.Equal(t, "jane@example.com", r.Email) - assert.Equal(t, "Jane Doe", r.FullName) - assert.Equal(t, "Admin", r.Role) -} diff --git a/pkg/accessreview/drivers/testdata/deel.yaml b/pkg/accessreview/drivers/testdata/deel.yaml deleted file mode 100644 index 49eda1054..000000000 --- a/pkg/accessreview/drivers/testdata/deel.yaml +++ /dev/null @@ -1,67 +0,0 @@ ---- -version: 2 -interactions: - - id: 0 - request: - proto: HTTP/1.1 - proto_major: 1 - proto_minor: 1 - content_length: 0 - host: api.letsdeel.com - form: - limit: - - "100" - offset: - - "0" - headers: - Accept: - - application/json - url: https://api.letsdeel.com/rest/v2/people?limit=100&offset=0 - method: GET - response: - proto: HTTP/2.0 - proto_major: 2 - proto_minor: 0 - content_length: -1 - uncompressed: true - body: '{"data":[{"id":"d_jane","email":"jane@example.com","first_name":"Jane","last_name":"Doe","job_title":"Engineering Lead","hiring_status":"active","start_date":"2024-06-01","end_date":""},{"id":"d_bob","email":"bob@example.com","first_name":"Bob","last_name":"Smith","job_title":"Designer","hiring_status":"inactive","start_date":"2023-01-15","end_date":"2025-09-30"}]}' - headers: - Content-Type: - - application/json - Date: - - Thu, 01 May 2026 12:00:00 GMT - status: 200 OK - code: 200 - duration: 100ms - - id: 1 - request: - proto: HTTP/1.1 - proto_major: 1 - proto_minor: 1 - content_length: 0 - host: api.letsdeel.com - form: - limit: - - "100" - offset: - - "100" - headers: - Accept: - - application/json - url: https://api.letsdeel.com/rest/v2/people?limit=100&offset=100 - method: GET - response: - proto: HTTP/2.0 - proto_major: 2 - proto_minor: 0 - content_length: -1 - uncompressed: true - body: '{"data":[]}' - headers: - Content-Type: - - application/json - Date: - - Thu, 01 May 2026 12:00:00 GMT - status: 200 OK - code: 200 - duration: 100ms diff --git a/pkg/accessreview/drivers/testdata/lever.yaml b/pkg/accessreview/drivers/testdata/lever.yaml deleted file mode 100644 index 2fd5b14a9..000000000 --- a/pkg/accessreview/drivers/testdata/lever.yaml +++ /dev/null @@ -1,33 +0,0 @@ ---- -version: 2 -interactions: - - id: 0 - request: - proto: HTTP/1.1 - proto_major: 1 - proto_minor: 1 - content_length: 0 - host: api.lever.co - form: - limit: - - "100" - headers: - Accept: - - application/json - url: https://api.lever.co/v1/users?limit=100 - method: GET - response: - proto: HTTP/2.0 - proto_major: 2 - proto_minor: 0 - content_length: -1 - uncompressed: true - body: '{"data":[{"id":"l_jane","email":"jane@example.com","name":"Jane Doe","accessRole":"super admin","deactivatedAt":null,"lastLoggedInAt":1745000000000,"createdAt":1717200000000},{"id":"l_bob","email":"bob@example.com","name":"Bob Smith","accessRole":"admin","deactivatedAt":1735000000000,"lastLoggedInAt":null,"createdAt":1717200000000}],"hasNext":false,"next":""}' - headers: - Content-Type: - - application/json - Date: - - Thu, 01 May 2026 12:00:00 GMT - status: 200 OK - code: 200 - duration: 100ms diff --git a/pkg/accessreview/drivers/testdata/ramp.yaml b/pkg/accessreview/drivers/testdata/ramp.yaml deleted file mode 100644 index 3370676d4..000000000 --- a/pkg/accessreview/drivers/testdata/ramp.yaml +++ /dev/null @@ -1,33 +0,0 @@ ---- -version: 2 -interactions: - - id: 0 - request: - proto: HTTP/1.1 - proto_major: 1 - proto_minor: 1 - content_length: 0 - host: api.ramp.com - form: - page_size: - - "100" - headers: - Accept: - - application/json - url: https://api.ramp.com/developer/v1/users?page_size=100 - method: GET - response: - proto: HTTP/2.0 - proto_major: 2 - proto_minor: 0 - content_length: -1 - uncompressed: true - body: '{"data":[{"id":"user-1","email":"jane@example.com","first_name":"Jane","last_name":"Doe","role":"BUSINESS_ADMIN","status":"USER_ACTIVE","last_login_at":"2026-04-15T10:00:00Z","is_manager":true},{"id":"user-2","email":"bob@example.com","first_name":"Bob","last_name":"Smith","role":"BUSINESS_USER","status":"USER_SUSPENDED","last_login_at":"","is_manager":false}],"page":{"next":""}}' - headers: - Content-Type: - - application/json - Date: - - Thu, 01 May 2026 12:00:00 GMT - status: 200 OK - code: 200 - duration: 100ms diff --git a/pkg/accessreview/drivers/testdata/snyk.yaml b/pkg/accessreview/drivers/testdata/snyk.yaml deleted file mode 100644 index 8c42734b7..000000000 --- a/pkg/accessreview/drivers/testdata/snyk.yaml +++ /dev/null @@ -1,35 +0,0 @@ ---- -version: 2 -interactions: - - id: 0 - request: - proto: HTTP/1.1 - proto_major: 1 - proto_minor: 1 - content_length: 0 - host: api.snyk.io - form: - limit: - - "100" - version: - - "2024-10-15" - headers: - Accept: - - application/vnd.api+json - url: https://api.snyk.io/rest/orgs/org-1234/memberships?version=2024-10-15&limit=100 - method: GET - response: - proto: HTTP/2.0 - proto_major: 2 - proto_minor: 0 - content_length: -1 - uncompressed: true - body: '{"data":[{"id":"membership-1","type":"org_membership","attributes":{"user":{"id":"user-1","email":"jane@example.com","name":"Jane Doe"},"role":{"id":"role-1","name":"Admin"}}},{"id":"membership-2","type":"org_membership","attributes":{"user":{"id":"user-2","email":"bob@example.com","name":"Bob Smith"},"role":{"id":"role-2","name":"Collaborator"}}}],"links":{"self":"/rest/orgs/org-1234/memberships?version=2024-10-15&limit=100"}}' - headers: - Content-Type: - - application/vnd.api+json - Date: - - Thu, 01 May 2026 12:00:00 GMT - status: 200 OK - code: 200 - duration: 100ms diff --git a/pkg/accessreview/review_engine.go b/pkg/accessreview/review_engine.go index 12edaf9b6..357c77ba0 100644 --- a/pkg/accessreview/review_engine.go +++ b/pkg/accessreview/review_engine.go @@ -423,15 +423,6 @@ func (e *ReviewEngine) resolveDriver( return nil, fmt.Errorf("asana connector requires workspace_gid in settings") } return drivers.NewAsanaDriver(httpClient, asanaSettings.WorkspaceGID), nil - case coredata.ConnectorProviderSnyk: - snykSettings, err := coredata.ConnectorSettings[coredata.SnykConnectorSettings](dbConnector) - if err != nil { - return nil, fmt.Errorf("cannot read snyk connector settings: %w", err) - } - if snykSettings.OrgID == "" { - return nil, fmt.Errorf("snyk connector requires org_id in settings") - } - return drivers.NewSnykDriver(httpClient, snykSettings.OrgID), nil case coredata.ConnectorProviderNetlify: netlifySettings, err := coredata.ConnectorSettings[coredata.NetlifyConnectorSettings](dbConnector) if err != nil { @@ -441,8 +432,6 @@ func (e *ReviewEngine) resolveDriver( return nil, fmt.Errorf("netlify connector requires account_slug in settings") } return drivers.NewNetlifyDriver(httpClient, netlifySettings.AccountSlug), nil - case coredata.ConnectorProviderRamp: - return drivers.NewRampDriver(httpClient), nil case coredata.ConnectorProviderClickUp: clickupSettings, err := coredata.ConnectorSettings[coredata.ClickUpConnectorSettings](dbConnector) if err != nil { @@ -463,10 +452,6 @@ func (e *ReviewEngine) resolveDriver( return drivers.NewVercelDriver(httpClient, vercelSettings.TeamID), nil case coredata.ConnectorProviderMonday: return drivers.NewMondayDriver(httpClient), nil - case coredata.ConnectorProviderLever: - return drivers.NewLeverDriver(httpClient), nil - case coredata.ConnectorProviderDeel: - return drivers.NewDeelDriver(httpClient), nil default: return nil, fmt.Errorf("unsupported connector provider %q for access source driver", dbConnector.Provider) } diff --git a/pkg/accessreview/source_name_worker.go b/pkg/accessreview/source_name_worker.go index 0bfdae6fa..e41f555bd 100644 --- a/pkg/accessreview/source_name_worker.go +++ b/pkg/accessreview/source_name_worker.go @@ -318,13 +318,6 @@ func (h *sourceNameHandler) buildResolver( return nil } return drivers.NewAsanaNameResolver(httpClient, asanaSettings.WorkspaceGID) - case coredata.ConnectorProviderSnyk: - snykSettings, err := coredata.ConnectorSettings[coredata.SnykConnectorSettings](dbConnector) - if err != nil { - h.logger.Error("cannot read snyk connector settings", log.Error(err)) - return nil - } - return drivers.NewSnykNameResolver(httpClient, snykSettings.OrgID) case coredata.ConnectorProviderNetlify: netlifySettings, err := coredata.ConnectorSettings[coredata.NetlifyConnectorSettings](dbConnector) if err != nil { @@ -332,8 +325,6 @@ func (h *sourceNameHandler) buildResolver( return nil } return drivers.NewNetlifyNameResolver(httpClient, netlifySettings.AccountSlug) - case coredata.ConnectorProviderRamp: - return drivers.NewRampNameResolver(httpClient) case coredata.ConnectorProviderClickUp: clickupSettings, err := coredata.ConnectorSettings[coredata.ClickUpConnectorSettings](dbConnector) if err != nil { @@ -350,10 +341,6 @@ func (h *sourceNameHandler) buildResolver( return drivers.NewVercelNameResolver(httpClient, vercelSettings.TeamID) case coredata.ConnectorProviderMonday: return drivers.NewMondayNameResolver(httpClient) - case coredata.ConnectorProviderLever: - return drivers.NewLeverNameResolver() - case coredata.ConnectorProviderDeel: - return drivers.NewDeelNameResolver(httpClient) default: return nil } diff --git a/pkg/bootstrap/builder.go b/pkg/bootstrap/builder.go index 8b3584746..2ded54299 100644 --- a/pkg/bootstrap/builder.go +++ b/pkg/bootstrap/builder.go @@ -355,13 +355,9 @@ func (b *Builder) Build() (*probodconfig.FullConfig, error) { "HEROKU", "PAGERDUTY", "ASANA", - "SNYK", "NETLIFY", - "RAMP", "CLICKUP", "MONDAY", - "LEVER", - "DEEL", } { clientID := b.getEnv("CONNECTOR_" + provider + "_CLIENT_ID") if clientID == "" { @@ -438,6 +434,15 @@ func (b *Builder) validateRequired() error { {"CONNECTOR_BREX", []string{"CLIENT_SECRET"}}, {"CONNECTOR_GOOGLE_WORKSPACE", []string{"CLIENT_SECRET"}}, {"CONNECTOR_MICROSOFT_365", []string{"CLIENT_SECRET"}}, + {"CONNECTOR_GITLAB", []string{"CLIENT_SECRET"}}, + {"CONNECTOR_BITBUCKET", []string{"CLIENT_SECRET"}}, + {"CONNECTOR_HEROKU", []string{"CLIENT_SECRET"}}, + {"CONNECTOR_PAGERDUTY", []string{"CLIENT_SECRET"}}, + {"CONNECTOR_ASANA", []string{"CLIENT_SECRET"}}, + {"CONNECTOR_NETLIFY", []string{"CLIENT_SECRET"}}, + {"CONNECTOR_CLICKUP", []string{"CLIENT_SECRET"}}, + {"CONNECTOR_MONDAY", []string{"CLIENT_SECRET"}}, + {"CONNECTOR_VERCEL", []string{"CLIENT_SECRET", "INTEGRATION_SLUG"}}, } for _, p := range oauthProviders { diff --git a/pkg/bootstrap/builder_test.go b/pkg/bootstrap/builder_test.go index 177dca30f..f4f5192ac 100644 --- a/pkg/bootstrap/builder_test.go +++ b/pkg/bootstrap/builder_test.go @@ -442,8 +442,7 @@ func TestBuilder_Build_AccessReviewConnectors(t *testing.T) { // CONNECTOR_VERCEL_INTEGRATION_SLUG env var. providers := []string{ "GITLAB", "BITBUCKET", "HEROKU", "PAGERDUTY", - "ASANA", "SNYK", "NETLIFY", "RAMP", "CLICKUP", - "MONDAY", "LEVER", "DEEL", + "ASANA", "NETLIFY", "CLICKUP", "MONDAY", } env := requiredEnv() diff --git a/pkg/connector/oauth2_test.go b/pkg/connector/oauth2_test.go index 4fba0dce8..59e9bb2e0 100644 --- a/pkg/connector/oauth2_test.go +++ b/pkg/connector/oauth2_test.go @@ -882,12 +882,12 @@ func TestGeneratePKCEVerifier(t *testing.T) { } // TestApplyProviderDefaults_PKCEDefaults asserts that the registered -// PAGERDUTY and SNYK provider defaults flip RequiresPKCE on so the -// downstream Initiate/Complete flow generates a verifier and replays it. +// PAGERDUTY provider defaults flip RequiresPKCE on so the downstream +// Initiate/Complete flow generates a verifier and replays it. func TestApplyProviderDefaults_PKCEDefaults(t *testing.T) { t.Parallel() - for _, provider := range []string{"PAGERDUTY", "SNYK"} { + for _, provider := range []string{"PAGERDUTY"} { t.Run(provider, func(t *testing.T) { t.Parallel() @@ -899,31 +899,6 @@ func TestApplyProviderDefaults_PKCEDefaults(t *testing.T) { } } -// TestApplyProviderDefaults_TokenExtraParamsDeepCopy guards against the -// shared-map aliasing bug class. Two connectors using the same provider -// (LEVER carries a non-empty TokenExtraParams) must not share the -// underlying map; mutating one must not be observable on the other or -// in the package-level providerDefinitions. -func TestApplyProviderDefaults_TokenExtraParamsDeepCopy(t *testing.T) { - t.Parallel() - - c1 := &OAuth2Connector{ClientID: "id1", ClientSecret: "s1"} - c2 := &OAuth2Connector{ClientID: "id2", ClientSecret: "s2"} - - ApplyProviderDefaults("LEVER", "https://example.com/cb", c1) - ApplyProviderDefaults("LEVER", "https://example.com/cb", c2) - - require.NotNil(t, c1.TokenExtraParams) - require.NotNil(t, c2.TokenExtraParams) - require.Equal(t, "https://api.lever.co/v1/", c1.TokenExtraParams["audience"]) - - c1.TokenExtraParams["sentinel"] = "mutated" - assert.NotContains(t, c2.TokenExtraParams, "sentinel", - "second connector must not see mutations on the first") - assert.NotContains(t, providerDefinitions["LEVER"].TokenExtraParams, "sentinel", - "shared providerDefinitions map must remain pristine") -} - // TestCompleteWithState_PKCEMismatch confirms that a token endpoint // rejecting a stale or mismatched code_verifier (the standard PKCE // failure path) surfaces as an error from CompleteWithState rather diff --git a/pkg/connector/providers.go b/pkg/connector/providers.go index 7a375d37a..e2f06b8c7 100644 --- a/pkg/connector/providers.go +++ b/pkg/connector/providers.go @@ -128,20 +128,10 @@ var ( AuthURL: "https://app.asana.com/-/oauth_authorize", TokenURL: "https://app.asana.com/-/oauth_token", }, - "SNYK": { - AuthURL: "https://app.snyk.io/oauth2/authorize", - TokenURL: "https://api.snyk.io/oauth2/token", - RequiresPKCE: true, - }, "NETLIFY": { AuthURL: "https://app.netlify.com/authorize", TokenURL: "https://api.netlify.com/oauth/token", }, - "RAMP": { - AuthURL: "https://app.ramp.com/v1/authorize", - TokenURL: "https://api.ramp.com/developer/v1/token", - TokenEndpointAuth: "basic-form", - }, "CLICKUP": { AuthURL: "https://app.clickup.com/api", TokenURL: "https://api.clickup.com/api/v2/oauth/token", @@ -159,31 +149,6 @@ var ( AuthURL: "https://auth.monday.com/oauth2/authorize", TokenURL: "https://auth.monday.com/oauth2/token", }, - // Lever runs on Auth0: the `audience` parameter is required in - // BOTH the authorize URL and the token-exchange POST body. The - // trailing slash on the audience value is mandatory. - "LEVER": { - AuthURL: "https://auth.lever.co/authorize", - TokenURL: "https://auth.lever.co/oauth/token", - ExtraAuthParams: map[string]string{ - "audience": "https://api.lever.co/v1/", - "prompt": "consent", - }, - TokenExtraParams: map[string]string{ - "audience": "https://api.lever.co/v1/", - }, - }, - // Deel: the token endpoint path is "/oauth2/tokens" (plural) — - // Deel's docs are inconsistent on the singular vs plural form. - // The API base host (api.letsdeel.com) differs from the auth host - // (app.deel.com). Deel's token endpoint requires HTTP Basic auth - // (base64(client_id:client_secret)); credentials placed in the - // form body are rejected with 401 invalid basic credentials. - "DEEL": { - AuthURL: "https://app.deel.com/oauth2/authorize", - TokenURL: "https://app.deel.com/oauth2/tokens", - TokenEndpointAuth: "basic-form", - }, } ) diff --git a/pkg/connector/registry.go b/pkg/connector/registry.go index deb4221de..f3e06bbf9 100644 --- a/pkg/connector/registry.go +++ b/pkg/connector/registry.go @@ -142,17 +142,13 @@ var ( "HEROKU": "https://api.heroku.com/account", "PAGERDUTY": "https://api.pagerduty.com/users/me", "ASANA": "https://app.asana.com/api/1.0/users/me", - "SNYK": "https://api.snyk.io/rest/self?version=2024-10-15", "NETLIFY": "https://api.netlify.com/api/v1/user", - "RAMP": "https://api.ramp.com/developer/v1/business", "CLICKUP": "https://api.clickup.com/api/v2/user", "VERCEL": "https://api.vercel.com/v2/user", // Monday's primary API is GraphQL POST, but the probe handler // is GET-only. Use the OIDC userinfo endpoint as a GET probe // that returns 200/401 with the same Bearer token. "MONDAY": "https://auth.monday.com/oauth2/userinfo", - "LEVER": "https://api.lever.co/v1/users?limit=1", - "DEEL": "https://api.letsdeel.com/rest/v2/people?limit=1", } ) diff --git a/pkg/coredata/connector_provider.go b/pkg/coredata/connector_provider.go index 76499c887..fa84aa6f5 100644 --- a/pkg/coredata/connector_provider.go +++ b/pkg/coredata/connector_provider.go @@ -45,14 +45,10 @@ const ( ConnectorProviderHeroku ConnectorProvider = "HEROKU" ConnectorProviderPagerDuty ConnectorProvider = "PAGERDUTY" ConnectorProviderAsana ConnectorProvider = "ASANA" - ConnectorProviderSnyk ConnectorProvider = "SNYK" ConnectorProviderNetlify ConnectorProvider = "NETLIFY" - ConnectorProviderRamp ConnectorProvider = "RAMP" ConnectorProviderClickUp ConnectorProvider = "CLICKUP" ConnectorProviderVercel ConnectorProvider = "VERCEL" ConnectorProviderMonday ConnectorProvider = "MONDAY" - ConnectorProviderLever ConnectorProvider = "LEVER" - ConnectorProviderDeel ConnectorProvider = "DEEL" ) func ConnectorProviders() []ConnectorProvider { @@ -79,14 +75,10 @@ func ConnectorProviders() []ConnectorProvider { ConnectorProviderHeroku, ConnectorProviderPagerDuty, ConnectorProviderAsana, - ConnectorProviderSnyk, ConnectorProviderNetlify, - ConnectorProviderRamp, ConnectorProviderClickUp, ConnectorProviderVercel, ConnectorProviderMonday, - ConnectorProviderLever, - ConnectorProviderDeel, } } @@ -150,22 +142,14 @@ func (cp *ConnectorProvider) Scan(value any) error { *cp = ConnectorProviderPagerDuty case "ASANA": *cp = ConnectorProviderAsana - case "SNYK": - *cp = ConnectorProviderSnyk case "NETLIFY": *cp = ConnectorProviderNetlify - case "RAMP": - *cp = ConnectorProviderRamp case "CLICKUP": *cp = ConnectorProviderClickUp case "VERCEL": *cp = ConnectorProviderVercel case "MONDAY": *cp = ConnectorProviderMonday - case "LEVER": - *cp = ConnectorProviderLever - case "DEEL": - *cp = ConnectorProviderDeel default: return fmt.Errorf("invalid ConnectorProvider value: %q", s) } diff --git a/pkg/coredata/connector_settings.go b/pkg/coredata/connector_settings.go index 221e6f9db..e31d3c1bf 100644 --- a/pkg/coredata/connector_settings.go +++ b/pkg/coredata/connector_settings.go @@ -70,10 +70,6 @@ type ( WorkspaceGID string `json:"workspace_gid"` } - SnykConnectorSettings struct { - OrgID string `json:"org_id"` - } - NetlifyConnectorSettings struct { AccountSlug string `json:"account_slug"` } diff --git a/pkg/coredata/migrations/20260508T729406Z.sql b/pkg/coredata/migrations/20260508T729406Z.sql index abc09da3d..9d648f656 100644 --- a/pkg/coredata/migrations/20260508T729406Z.sql +++ b/pkg/coredata/migrations/20260508T729406Z.sql @@ -17,11 +17,7 @@ ALTER TYPE connector_provider ADD VALUE IF NOT EXISTS 'BITBUCKET'; ALTER TYPE connector_provider ADD VALUE IF NOT EXISTS 'HEROKU'; ALTER TYPE connector_provider ADD VALUE IF NOT EXISTS 'PAGERDUTY'; ALTER TYPE connector_provider ADD VALUE IF NOT EXISTS 'ASANA'; -ALTER TYPE connector_provider ADD VALUE IF NOT EXISTS 'SNYK'; ALTER TYPE connector_provider ADD VALUE IF NOT EXISTS 'NETLIFY'; -ALTER TYPE connector_provider ADD VALUE IF NOT EXISTS 'RAMP'; ALTER TYPE connector_provider ADD VALUE IF NOT EXISTS 'CLICKUP'; ALTER TYPE connector_provider ADD VALUE IF NOT EXISTS 'VERCEL'; ALTER TYPE connector_provider ADD VALUE IF NOT EXISTS 'MONDAY'; -ALTER TYPE connector_provider ADD VALUE IF NOT EXISTS 'LEVER'; -ALTER TYPE connector_provider ADD VALUE IF NOT EXISTS 'DEEL'; diff --git a/pkg/server/api/console/v1/access_source_provider_config.go b/pkg/server/api/console/v1/access_source_provider_config.go index 31065108f..c3bebd6b8 100644 --- a/pkg/server/api/console/v1/access_source_provider_config.go +++ b/pkg/server/api/console/v1/access_source_provider_config.go @@ -92,14 +92,6 @@ var providerOrgConfigs = map[coredata.ConnectorProvider]providerOrgConfig{ }, NeedsPicker: true, }, - coredata.ConnectorProviderSnyk: { - ListOrgs: drivers.ListSnykOrganizations, - SelectedSlug: func(c *coredata.Connector) string { - s, _ := coredata.ConnectorSettings[coredata.SnykConnectorSettings](c) - return s.OrgID - }, - NeedsPicker: true, - }, coredata.ConnectorProviderNetlify: { ListOrgs: drivers.ListNetlifyOrganizations, SelectedSlug: func(c *coredata.Connector) string { diff --git a/pkg/server/api/console/v1/graphql/connector.graphql b/pkg/server/api/console/v1/graphql/connector.graphql index 43beeb69f..3de9c2aa9 100644 --- a/pkg/server/api/console/v1/graphql/connector.graphql +++ b/pkg/server/api/console/v1/graphql/connector.graphql @@ -38,16 +38,12 @@ enum ConnectorProvider PAGERDUTY @goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderPagerDuty") ASANA @goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderAsana") - SNYK @goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderSnyk") NETLIFY @goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderNetlify") - RAMP @goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderRamp") CLICKUP @goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderClickUp") VERCEL @goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderVercel") MONDAY @goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderMonday") - LEVER @goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderLever") - DEEL @goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderDeel") } type ConnectorProviderInfo {