Replace the per-approver add/remove model with a quorum-based approval
system. Documents now have default approvers that are pre-populated when
requesting approval, and the publish dialog lets users adjust the list
before submitting.
Key changes:
- Add PENDING_APPROVAL document version status with dedicated transitions
- Introduce approval quorums with request/approve/reject/void lifecycle
- Add default approvers per document (stored in document_default_approvers)
with MERGE-based upsert for efficient sync
- Add NoDuplicates validator for slice fields
- Split ALTER TYPE ADD VALUE migrations into separate files (required by
PostgreSQL when run inside transactions)
- Use VOIDED consistently for both quorum status and decision state enums
- Expose void/approve/reject through GraphQL and MCP, with e2e tests
- Add approval management UI: publish dialog with approver selection,
approval list with void support, and external approve/reject page
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
Rename the entity across the full stack: database table
(states_of_applicability → statements_of_applicability), Go model,
GraphQL types, MCP specification, CLI commands, frontend components,
routes, and display labels. Includes a migration to rename the table
and its foreign key column.
Widen sidebar from 260px to 280px to fit the longer label.
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
Use utf8.RuneCountInString so multi-byte characters like é count as one
character. Add a unicode test case to cover this.
Signed-off-by: Émile Ré <emile@getprobo.com>
Add Node.TextLength() to walk the ProseMirror tree and sum actual user
text. Introduce ProseMirrorDocumentMaxTextLength validator that enforces
a 50k character limit on extracted text, paired with a 500k byte safety
cap on the raw JSON string. Extend e2e tests for content length.
Signed-off-by: Émile Ré <emile@getprobo.com>
The MCP UpdateRiskTool resolver was not passing the Note field
to the UpdateRiskRequest, causing note updates via MCP to be
silently ignored. The GraphQL resolver already forwarded it
correctly.
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
A Google-Workspace-specific field on the generic Organization type
was future-hostile: each new SCIM bridge type would need its own
top-level field. Replace with a generic SCIMBridgeTypeInfo type
queried through Organization.scimBridgeTypes, parallel to the
ConnectorProviderInfo pattern in console/v1.
ConnectorList looks up the Google Workspace entry from the list
and passes its scopes to GoogleWorkspaceConnector as before.
Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
The AccessSource.oauth2Scopes field duplicated knowledge that
naturally belongs on the Connector object that AccessSource
already exposes via its connector field. Move it to Connector so
every type that holds a connector (AccessSource, SCIMBridge, etc.)
can reach the scopes through the connector relationship.
AccessSourceRow now queries accessSource.connector { oauth2Scopes }
in its reconnect flow.
Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
Add per-context fields so the frontend can read scopes from the
type that owns each connection:
- ConnectorProviderInfo.oauth2Scopes: access review providers
- AccessSource.oauth2Scopes: access review reconnect flow
- Organization.slackOAuth2Scopes (console): compliance page Slack
- Organization.googleWorkspaceOAuth2Scopes (connect): SCIM bridge
Resolvers delegate to the constants declared in each owning Go
module. The Google Workspace field lives on Organization, not on
SCIMConfiguration, so the Connect button can read it before any
SCIM configuration exists.
Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
Each module that initiates an OAuth2 flow now declares its scopes
in its own package instead of duplicating them in the frontend or
in shared connector config:
- pkg/accessreview/drivers: per-provider scopes for the access
review drivers
- pkg/slack: scopes for the compliance page integration
- pkg/iam/scim/bridge/provider/googleworkspace: scopes for the
SCIM provisioning bridge
These constants are surfaced to the frontend via GraphQL fields
so the frontend never hardcodes scope strings.
Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
Add an InitiateOptions struct to the Connector interface so each
caller can declare the scopes it needs instead of having them baked
into the connector at registration. The HTTP handler reads repeated
?scope= query parameters from /connectors/initiate and forwards them.
Also restore GOOGLE_WORKSPACE and LINEAR provider definitions which
were silently dropped from the bootstrap config refactor.
Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
Use the ContinueURL from the state token so the user is redirected
back to where they initiated the flow instead of the root URL.
The redirect is safe because safeRedirect validates the host.
Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
When a provider returns an error (e.g. user denies consent), the
callback now logs the error with provider name and redirects to
the base URL with error and error_description query parameters
instead of falling through to the code exchange.
Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
Reduce closure size in NewMux by extracting the /connectors/complete
handler into a dedicated handleConnectorComplete function. Cache
r.URL.Query() into a local variable to avoid repeated parsing.
Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
Drop RedirectURI, AuthURL, TokenURL, Scopes, ExtraAuthParams, and
TokenEndpointAuth from all connector config blocks. Remove
REDIRECT_URI from env var validation. Fix error wrapping in SAML
credential helpers.
Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
Compute the OAuth2 redirect URI from the base URL using the
CallbackPath constant and apply provider defaults before
registering each connector.
Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
All other OAuth2 properties (redirect URI, auth URL, token URL,
scopes, extra params, token endpoint auth) now come from the
connector package provider definitions at wiring time.
Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
Remove provider-default mutation from Register; defaults are now
applied via ApplyProviderDefaults before registration. Rename
receiver from cr to r. Fix error messages to follow the cannot
convention. Wrap providerProbeURLs in var () block.
Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
Centralise static OAuth2 properties (auth URL, token URL, scopes,
extra params, token endpoint auth) per provider in a single map.
This removes the need to duplicate these values in deployment
config; only ClientID and ClientSecret remain configurable.
Introduces ApplyProviderDefaults to set redirect URI and provider
defaults onto an OAuth2Connector at wiring time.
Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
The NewTask type helper was not copying OrganizationID from the
coredata Task, causing it to serialize as a zero-value GID.
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
Introduce the ability to link measures to documents, following the
existing pattern used by controls and risks. This includes:
- Database migration for measures_documents join table
- Coredata MeasureDocument struct with insert/delete operations
- Document service methods for listing/counting by measure ID
- Measure service CreateDocumentMapping/DeleteDocumentMapping methods
- Cleanup of measure-document mappings on document archive
- GraphQL mutations, inputs, payloads, and Measure.documents field
- DocumentConnection.TotalCount support for measure resolver
- MCP linkMeasure/unlinkMeasure updated to support documents
- MCP listMeasureDocuments tool
- Frontend MeasureDocumentsTab with LinkedDocumentsCard integration
- Authorization actions for measure document mapping
- E2e tests for measure document mapping
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
Filter out approval decisions where state is APPROVED but
electronic_signature_id is NULL, removing historical approved
entries that lack an electronic signature from the employee
approvals page.
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
Allow publishing a document version even when the content and title
are identical to the current published version.
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
Replace the monolithic agents config with a cleaner structure:
- llm: holds provider credentials and default model settings
- probo-agent: LLM overrides for the probo agent
- evidence-describer: worker config (interval, stale-after,
max-concurrency) alongside LLM overrides
This makes worker tuning configurable via YAML and env vars
instead of being hardcoded in Go, and separates provider
credentials from per-consumer model settings.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
Introduce IN_PROGRESS as a new task state between TODO and DONE across
the full stack: database enum, Go backend, GraphQL, MCP, and frontend.
The task state icon now cycles forward on click (TODO → IN_PROGRESS →
DONE → TODO), and the action dropdown provides explicit "Move to"
options for any state transition. The "All" tab supports drag-and-drop
between state sections to change a task's state.
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
Follow the same pattern used for classification: document type now lives
exclusively on DocumentVersion. A migration copies existing values from
documents to their versions. The document filter uses a subquery on the
latest version. All three API surfaces (GraphQL, MCP, CLI), resolvers,
frontend, and e2e tests are updated accordingly.
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
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>
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 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 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 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>
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>