Skip to content

Add access review campaigns#935

Merged
gearnode merged 13 commits intogetprobo:mainfrom
aureliensibiril:aureliensibiril/eng-136-access-review
Apr 2, 2026
Merged

Add access review campaigns#935
gearnode merged 13 commits intogetprobo:mainfrom
aureliensibiril:aureliensibiril/eng-136-access-review

Conversation

@aureliensibiril
Copy link
Copy Markdown
Collaborator

@aureliensibiril aureliensibiril commented Mar 26, 2026

Summary

Add access review campaigns with full lifecycle management (create, start, close, cancel) and a source fetch worker that pulls account data from connected providers.

Access source drivers (18 providers)

Provider Connection Fields
Google Workspace OAuth2 Email, name, role, admin, MFA, last login, created at
Slack OAuth2 Email, name, role, job title, admin, MFA, bot support
Linear OAuth2 Email, name, role, admin, active, created at
Cloudflare API key Email, name, roles (concatenated), MFA, active
Brex API key Email, name, role, active
Tally API key Email, name, role
1Password API key Email, name, role, active, MFA, created at
DocuSign OAuth2 Email, name, role, job title, admin, active, created at
HubSpot OAuth2 Email, name, role (resolved via roles endpoint)
Notion OAuth2 Email, name, role, bot support (ServiceAccount)
Figma OAuth2 Email, name
OpenAI API key Email, name, role, MFA
Sentry API key Email, name, role, MFA, active
GitHub OAuth2 Email, name, role, admin, MFA, 2FA status
Supabase API key Email, name, role, MFA, active
Intercom OAuth2 Email, name, role
Resend API key Email, name, role
Probo memberships Internal Email, name, role, admin, active, created at
CSV File upload Email, name, role, job title, admin, active, external ID

API surface (GraphQL + MCP + CLI)

  • Campaign CRUD, start, close, cancel
  • Access source CRUD with OAuth2 and API key connector support
  • Campaign scope source management (add/remove)
  • Access entry listing, flagging, and decision recording (approve/revoke)
  • Bulk decide-all for batch processing

Console UI

  • Campaign list with pagination and status badges
  • Campaign detail page with entry table (flag, decision columns)
  • Access source creation page with OAuth2 flow and API key dialog
  • CSV upload source creation
  • Vendor icon components for all supported providers

Bug fixes included

  • Fix source name worker retry on resolution failure
  • Fix React hooks ordering in access source page (lint)
  • Fix connector dropdown showing non-unique labels
  • Fix OAuth callback retry when mutation fails
  • Handle DocuSign pagination parse errors
  • Return error when driver pagination limit is reached
  • Accept variable-length rows in CSV driver
  • Fix scope sources query returning all org sources after removal

Test plan

  • make test passes
  • make lint passes (0 errors)
  • make test-e2e — access review e2e tests pass
  • Create a campaign via CLI, add a source, start it, verify source fetch completes
  • Verify campaign lifecycle transitions (draft → in_progress → pending_actions → completed)
  • Test cancel and delete flows

@aureliensibiril aureliensibiril marked this pull request as draft March 26, 2026 09:53
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

11 issues found across 118 files

Note: This PR contains a large number of files. cubic only reviews up to 75 files per PR, so some files may not have been reviewed.

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/console/src/pages/organizations/access-reviews/dialogs/CreateAccessSourceDialog.tsx">

<violation number="1" location="apps/console/src/pages/organizations/access-reviews/dialogs/CreateAccessSourceDialog.tsx:56">
P2: CreateAccessSourceDialog only allows Google Workspace/Linear OAuth providers, but the access review flow supports Slack as an OAuth provider. Slack connectors are filtered out and cannot be selected, preventing Slack access sources from being created via this dialog.</violation>

<violation number="2" location="apps/console/src/pages/organizations/access-reviews/dialogs/CreateAccessSourceDialog.tsx:286">
P2: Connector dropdown labels are non-unique (provider-only), making same-provider connectors indistinguishable and prone to wrong selection.</violation>
</file>

<file name="pkg/accessreview/drivers/docusign.go">

<violation number="1" location="pkg/accessreview/drivers/docusign.go:92">
P2: Ignored strconv.Atoi errors can turn pagination metadata into 0, causing the loop to break early and silently truncate user enumeration.</violation>
</file>

<file name="apps/console/src/pages/organizations/access-reviews/AccessReviewLayoutLoader.tsx">

<violation number="1" location="apps/console/src/pages/organizations/access-reviews/AccessReviewLayoutLoader.tsx:15">
P2: The loader only runs loadQuery when queryRef is null, so changing organizationId while the layout stays mounted will not reload the query and can show stale org data. Remove the guard or otherwise reload when organizationId changes.</violation>
</file>

<file name="pkg/accessreview/source_name_worker.go">

<violation number="1" location="pkg/accessreview/source_name_worker.go:96">
P1: The worker sets `NameSyncedAt` before name resolution succeeds, and subsequent swallowed errors prevent retries because only `name_synced_at IS NULL` rows are selected.</violation>
</file>

<file name="pkg/accessreview/drivers/csv.go">

<violation number="1" location="pkg/accessreview/drivers/csv.go:41">
P2: CSV reader enforces a fixed field count based on the header, so any row with fewer/more columns aborts the import before the `idx < len(row)` guards can run. If optional columns are allowed, set `FieldsPerRecord = -1` to accept variable-length rows.</violation>
</file>

<file name="pkg/accessreview/drivers/driver.go">

<violation number="1" location="pkg/accessreview/drivers/driver.go:44">
P2: Pagination is capped at 500 pages, but drivers return results silently when the cap is hit. If `NextCursor` is still present after 500 iterations, `ListAccounts` returns partial data without an error, which can truncate large tenants.</violation>
</file>

<file name="apps/console/src/pages/organizations/access-reviews/CreateAccessSourcePage.tsx">

<violation number="1" location="apps/console/src/pages/organizations/access-reviews/CreateAccessSourcePage.tsx:241">
P2: OAuth callback creation can get stuck after a network failure: the connector_id remains in the URL and processedConnectorIdRef is never cleared on error, so subsequent renders skip retrying the mutation until a full page reload.</violation>
</file>

<file name="pkg/cmd/accessreview/entry/decide/decide.go">

<violation number="1" location="pkg/cmd/accessreview/entry/decide/decide.go:132">
P1: Custom agent: **Avoid Logging Sensitive Information**

Remove the email address from the CLI output to avoid logging PII.</violation>
</file>

<file name="pkg/cmd/accessreview/entry/flag/flag.go">

<violation number="1" location="pkg/cmd/accessreview/entry/flag/flag.go:130">
P1: Custom agent: **Avoid Logging Sensitive Information**

Do not print the access entry email to stdout; it is PII and violates the "Avoid Logging Sensitive Information" rule.</violation>
</file>

<file name="pkg/cmd/accessreview/entry/decideall/decideall.go">

<violation number="1" location="pkg/cmd/accessreview/entry/decideall/decideall.go:129">
P1: Custom agent: **Avoid Logging Sensitive Information**

Remove email addresses from CLI output; this change logs PII (`e.Email`) for each access entry.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread pkg/accessreview/source_name_worker.go Outdated
Comment thread pkg/cmd/access-review/entry/decide/decide.go Outdated
Comment thread pkg/cmd/access-review/entry/setflag/flag.go Outdated
Comment thread pkg/cmd/access-review/entry/decideall/decideall.go Outdated
Comment thread pkg/accessreview/drivers/docusign.go Outdated
Comment thread pkg/accessreview/drivers/csv.go
Comment thread pkg/accessreview/drivers/driver.go
Comment thread apps/console/src/pages/organizations/access-reviews/CreateAccessSourcePage.tsx Outdated
@aureliensibiril aureliensibiril force-pushed the aureliensibiril/eng-136-access-review branch 3 times, most recently from 2e423e5 to 128164a Compare March 27, 2026 15:51
@aureliensibiril aureliensibiril marked this pull request as ready for review March 30, 2026 12:40
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

8 issues found across 204 files

Note: This PR contains a large number of files. cubic only reviews up to 75 files per PR, so some files may not have been reviewed.

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/console/src/pages/organizations/access-reviews/_components/AccessSourceRow.tsx">

<violation number="1" location="apps/console/src/pages/organizations/access-reviews/_components/AccessSourceRow.tsx:297">
P2: Per-row `useLazyLoadQuery` in `InlineOrgSelect` causes N+1 GraphQL requests in the access source table, which can degrade performance as row count grows.</violation>
</file>

<file name="pkg/accessreview/drivers/name_resolver.go">

<violation number="1" location="pkg/accessreview/drivers/name_resolver.go:156">
P2: Non-2xx provider responses are treated as successful resolution ("", nil), so transient API failures get marked as synced and never retried.</violation>
</file>

<file name="apps/console/src/pages/organizations/access-reviews/dialogs/CreateAccessReviewCampaignDialog.tsx">

<violation number="1" location="apps/console/src/pages/organizations/access-reviews/dialogs/CreateAccessReviewCampaignDialog.tsx:58">
P2: Source selection is limited to the first 100 access sources with no pagination path, so larger organizations cannot select sources beyond that first page when creating a campaign.</violation>

<violation number="2" location="apps/console/src/pages/organizations/access-reviews/dialogs/CreateAccessReviewCampaignDialog.tsx:143">
P2: Dialog form/source state is only reset after successful submit, so dismissing and reopening can retain stale values.</violation>
</file>

<file name="apps/console/src/pages/organizations/access-reviews/sources/AccessReviewSourcesTab.tsx">

<violation number="1" location="apps/console/src/pages/organizations/access-reviews/sources/AccessReviewSourcesTab.tsx:153">
P2: Error handlers reset `processedConnectorIdRef` but leave `connector_id`/`provider` in the URL, so the OAuth callback effect re-triggers after the mutation completes and re-issues `createAccessSource` indefinitely on persistent errors.</violation>
</file>

<file name="apps/console/src/pages/organizations/access-reviews/campaigns/CampaignDetailPage.tsx">

<violation number="1" location="apps/console/src/pages/organizations/access-reviews/campaigns/CampaignDetailPage.tsx:148">
P2: Entries are hard-limited to the first 50 and the UI only shows a notice when `hasNextPage` is true; there is no pagination or load-more path, so users cannot review/decide entries beyond the first 50 in this page.</violation>

<violation number="2" location="apps/console/src/pages/organizations/access-reviews/campaigns/CampaignDetailPage.tsx:222">
P2: `pendingEntryCount` is used to enable completion, but it isn't refreshed in the pending-actions flow; decision mutations only update entries and there's no refetch/polling outside IN_PROGRESS, so the completion button can stay disabled until manual refresh.</violation>

<violation number="3" location="apps/console/src/pages/organizations/access-reviews/campaigns/CampaignDetailPage.tsx:233">
P2: Bulk flagging always shows success even if some flag mutations fail; errors are ignored, so users can be misled about whether flags were applied.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread pkg/accessreview/drivers/name_resolver.go
Comment thread pkg/coredata/access_entry.go Outdated
Comment thread pkg/cmd/access-review/entry/list/list.go Outdated
Comment thread pkg/probo/review_engine.go Outdated
Comment thread pkg/coredata/migrations/20260314T200000Z.sql Outdated
Comment thread pkg/accessreview/drivers/github.go
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 1 file (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="pkg/accessreview/drivers/github.go">

<violation number="1" location="pkg/accessreview/drivers/github.go:63">
P1: Custom agent: **Avoid Logging Sensitive Information**

Avoid logging member identifiers (`m.Login`) in warnings; this exposes PII in logs and violates the Avoid Logging Sensitive Information rule.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread pkg/accessreview/drivers/github.go Outdated
@aureliensibiril aureliensibiril force-pushed the aureliensibiril/eng-136-access-review branch from 877cce5 to 31a2ae4 Compare March 30, 2026 13:46
Comment thread pkg/accessreview/worker.go Outdated
Comment thread pkg/accessreview/worker.go Outdated
Comment thread pkg/accessreview/worker.go Outdated
Comment thread pkg/accessreview/worker.go Outdated
Comment thread pkg/statelesstoken/statelesstoken.go
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 31 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/console/src/pages/organizations/access-reviews/_components/AccessSourceRow.tsx">

<violation number="1" location="apps/console/src/pages/organizations/access-reviews/_components/AccessSourceRow.tsx:211">
P2: Removing the fallback to window.location.origin means reconnect will throw if VITE_API_URL is unset, breaking the action at runtime. Other flows in this codebase still rely on the fallback, so this change can regress environments where the env var is missing.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 1 file (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/console/src/pages/organizations/access-reviews/campaigns/CampaignDetailPage.tsx">

<violation number="1" location="apps/console/src/pages/organizations/access-reviews/campaigns/CampaignDetailPage.tsx:223">
P1: Completion is incorrectly gated by paginated/truncated client data, which can permanently disable campaign completion for valid campaigns.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread pkg/accessreview/campaign_service.go Outdated
Comment thread pkg/accessreview/campaign_service.go Outdated
Comment thread pkg/accessreview/campaign_service.go Outdated
@aureliensibiril aureliensibiril force-pushed the aureliensibiril/eng-136-access-review branch 6 times, most recently from 3c60bb7 to 465c938 Compare April 2, 2026 09:52
Add coredata entities for access review campaigns,
access sources, access entries with decision history,
campaign source fetches, and scope systems. Include
migrations, entity type registrations, enum types for
flags, decisions, MFA status, and auth methods.

Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
Add AccessSourceService, AccessEntryService,
CampaignService, and ReviewEngine in the accessreview
package. Service exposes tenant-scoped sub-service
accessors and an unscoped ResolveEntryOrganizationID.
Register access review actions and policies.

Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
Add SourceFetchWorker for campaign source fetching
with bounded concurrency and SourceNameWorker for
resolving provider instance names via OAuth connectors.

Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
Add Driver interface and implementations for Google
Workspace, Linear, Slack, 1Password, HubSpot, DocuSign,
Notion, Brex, Tally, Cloudflare, CSV, Probo memberships,
Sentry, OpenAI, Supabase, GitHub, Intercom, and Resend.
Include name resolvers, VCR test infrastructure with
cassettes, and RFC 5988 link header parser.

Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
Extract link header parsing into a reusable pkg/rfc5988
package with Parse and FindByRel functions, used by
Sentry and GitHub drivers for pagination.

Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
Add API key connector protocol, OAuth2 client credentials
grant, token refresh config, provider info endpoint,
ConnectorProviders helper, and bootstrap configs for all
OAuth providers. Move OAuth2 state decode near type.

Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
Add queries, mutations, and types for access review
campaigns, access sources, access entries with decisions
and flags, connector provider info, and provider org
listing. Wire accessreview.Service into the Resolver.

Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
Add MCP tool definitions and resolvers for access review
campaigns, sources, entries, decisions, and flags. Wire
accessreview.Service into the MCP Resolver.

Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
Add prb access-review subcommands for campaigns (create,
update, delete, list, view, start, close, cancel, add/
remove source) entries (list, decide, decide-all, flag)
and sources (create, update, delete, list, view).

Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
Add e2e tests for access review API and connector
operations covering RBAC, tenant isolation, and the
full campaign lifecycle.

Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
Add AccessReview field to server.Config and api.Config,
pass through to console and MCP NewMux. Create the
service in probod and run its background workers.

Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
Add campaign and source management pages with detail
views, bulk decision and flag controls, connector
provider dialog with OAuth/API-key/client-credentials
flows, vendor logos, shared helpers, and campaign
lifecycle UX (start, complete, cancel).

Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
@aureliensibiril aureliensibiril force-pushed the aureliensibiril/eng-136-access-review branch from 465c938 to 6974041 Compare April 2, 2026 12:38
Add go-vcr dependency, dev config for new providers,
connector service changes for access review, connect
schema updates, and unit tests for enum Scan/Value.

Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
@aureliensibiril aureliensibiril force-pushed the aureliensibiril/eng-136-access-review branch from 6974041 to ff20b38 Compare April 2, 2026 12:49
@gearnode gearnode merged commit ff20b38 into getprobo:main Apr 2, 2026
11 checks passed
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