Replace the old snapshot-based system for vendors with the publish
document system, mirroring the prior processing activity / DPIA / TIA
migration. Includes the GraphQL mutation, MCP tool, CLI command, n8n
operation, frontend publish dialog, e2e tests, and a prosemirror
register template covering vendor profile fields plus per-vendor
sections for services, contacts, risk assessments, compliance reports,
BAA and DPA agreements.
The vendor register lives as a generated DocumentTypeRegister document
on the organization, reused across publishes (the major version bumps
on every republish). Approvers can be passed in to create a draft
pending approval; otherwise the version is published immediately. The
frontend Vendors page exposes a Publish button and a Document link
button when the document exists, and pre-fills the previous default
approvers.
Remove snapshot mode entirely from vendors and their sub-entities: drop
snapshotId/sourceId from GraphQL Vendor type and VendorFilter; remove
SnapshotsTypeVendors from the snapshot registry and delete
Vendors.Snapshot, VendorSnapshotter interface and all
*.InsertVendorSnapshots methods on contacts, services, risk
assessments, compliance reports, BAA and DPA. Drop the snapshot routes
and banner from the frontend. The snapshot_id columns remain in the
database but are now filtered out with snapshot_id IS NULL.
Add Get/Upsert/Clear GeneratedDocumentID methods on Vendor backed by a
new vendors_document_id column on generated_documents, matching the
ProcessingActivity/Finding/Obligation pattern.
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
RFC 7662 lets clients introspect any OAuth2 token, but the endpoint
only resolved access tokens. Look up refresh tokens too, honor the
optional token_type_hint to drive lookup order with a fallback to the
other table, and report revoked or expired refresh tokens as inactive.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
Session validity was decoupled from credential rotation: changing
a password (logged-in flow) or completing a forgot-password reset
left every existing iam_sessions row valid until its idle TTL.
A user who saw their account compromised on another device had
no way to actually evict that device by rotating the password.
Inside the same DB transaction as the password update, expire the
identity's other active sessions:
- ChangePassword keeps the caller's current session and revokes
every other session for the identity, so the user is not
logged out of the browser they just used.
- ResetPassword has no caller session (the user is anonymous,
authenticated only by a stateless token) and revokes all of
the identity's active sessions.
The session middleware already rejects rows with expire_reason
set, so revoked sessions are kicked out on the next request
without any middleware change.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
The SCIM event handler read the client address from
r.RemoteAddr only, so when probod runs behind an HTTP
load balancer every event row recorded the load
balancer's internal IP. Switch to clientip.Extract,
which honors the Forwarded / X-Forwarded-For headers
already validated by trustedproxy middleware.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
A client can prepend a spoofed entry to X-Forwarded-For before
the request reaches our load balancer. Taking the first value
would return the attacker's address. Since we sit behind a
single trusted LB that appends the real client IP as the last
entry, switch to rightmost extraction for both X-Forwarded-For
and RFC 7239 Forwarded headers.
Signed-off-by: Émile Ré <emile@getprobo.com>
probod-bootstrap only needs the config struct definitions for
YAML marshaling but transitively pulled in ~40 heavy runtime
dependencies via pkg/probod. Move all config types and their
methods to a new pkg/probodconfig package and re-export them
from pkg/probod via type aliases for backward compatibility.
Signed-off-by: Émile Ré <emile@getprobo.com>
Replace the old PDF/snapshot-based exports for processing activities,
Data Protection Impact Assessments and Transfer Impact Assessments with
the publish document system. Includes GraphQL mutations, MCP tools, CLI
commands, n8n operations, frontend publish dialogs, e2e tests, and
prosemirror register templates that mirror the previous PDF layouts.
Each register lives as a generated DocumentTypeRegister document on the
organization, reused across publishes (the major version bumps on every
republish). Approvers can be passed in to create a draft pending
approval; otherwise the version is published immediately. The frontend
ProcessingActivities page exposes a Publish dropdown per register and a
Document link button per active tab, pre-fills the previous default
approvers, and navigates to the published document on success.
Remove snapshot mode entirely from these three entities: drop snapshotId
and sourceId from GraphQL schemas, types, filters, resolvers, MCP spec,
frontend routes and pages; remove SnapshotsTypeProcessingActivities from
the snapshot registry and delete the ProcessingActivities.Snapshot,
ProcessingActivitySnapshotter interface and *.InsertProcessingActivitySnapshots
methods. The snapshot_id columns remain in the database but are now
filtered out with snapshot_id IS NULL.
Add Get/Upsert/Clear GeneratedDocumentID methods on each entity type
(ProcessingActivity, DataProtectionImpactAssessment,
TransferImpactAssessment) backed by new columns in the generated_documents
table, matching the Finding/Obligation pattern.
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
Add strict numeric validation to all version filter refetch
paths and include orderBy in @connection filters to prevent
cache collisions across sort orders.
Signed-off-by: Émile Ré <emile@getprobo.com>
Batch-load CookieBanner and CookieCategory entities via
dataloadgen instead of making individual service calls in
GraphQL resolvers, matching the existing dataloader pattern
used for organizations, frameworks, etc.
Signed-off-by: Émile Ré <emile@getprobo.com>
Resolvers for CookieCategory, Organization, CookieBanner,
Translations, and CookieBannerVersion.Categories were either
missing authorization checks or returning ID-only stubs
without querying the database. This fixes both issues by
adding proper authorize calls and fetching full entities.
Signed-off-by: Émile Ré <emile@getprobo.com>
Display record attributes and parsed consent data with
per-category consent state and cookies from the banner
version snapshot. The page lives outside the config layout
with its own breadcrumb navigation.
Signed-off-by: Émile Ré <emile@getprobo.com>
Replace the opaque cookieBannerVersionId filter with an
integer version filter. The SQL filter now resolves the
version number via a subquery against cookie_banner_versions.
Also fix the CookieBannerVersion resolver on consent records
to load the full version from the database instead of
returning a stub with only the ID set (which caused the
version to always display as 0).
Signed-off-by: Émile Ré <emile@getprobo.com>
Exposes the cookie consent record audit trail through a new
"Consent Records" tab on the cookie banner configuration page.
The full stack includes: extended coredata filter (visitor ID,
banner version), GraphQL schema/types/resolvers, and a React
page with SortableTable (size 50) and three compliance filters
(action, visitor ID, banner version).
Signed-off-by: Émile Ré <emile@getprobo.com>
Move JS snippet (CodeSnippets) into Settings, move category
sorting (CategoryList) into the renamed Display page, remove
the blocking-scripts step, and rename the Snippet tab to Display.
Signed-off-by: Émile Ré <emile@getprobo.com>
Replace the old snapshot-based approach with the new publish document
system for findings and obligations. Includes GraphQL mutations, MCP
tools, CLI commands, e2e tests, frontend publish dialogs, and
snapshot-to-document migration tools.
Remove snapshot mode entirely from findings and obligations: drop
snapshotId from GraphQL schemas, filters, resolvers, MCP spec, frontend
routes, pages, and helpers. The snapshot_id column remains in the
database but is now filtered out with snapshot_id IS NULL.
Remove auditor's ability to publish SoA.
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
Replace the --active boolean flag with two explicit filters:
--state (ACTIVE/INACTIVE) and --contract-ended (true/false).
Also add state filter support to the console GraphQL API.
Signed-off-by: Émile Ré <emile@getprobo.com>
Add the profile state attribute (ACTIVE/INACTIVE) to the MCP
Profile schema so listUsers and getUser tools expose it, and
add a state filter to listUsers.
Rename excludeContractEnded to contractEnded across the entire
stack (MCP, GraphQL, CLI, frontend). The new boolean is two-way:
true returns only users with ended contracts, false returns only
users with active or no contract, and null returns all.
Signed-off-by: Émile Ré <emile@getprobo.com>
Move the cookie-banner SDK version from the POST consents
request body to a custom X-SDK-Version header sent on every
API call. The server now reads it from the header and the
CORS middleware allows it through preflight.
Signed-off-by: Émile Ré <emile@getprobo.com>
Notion was the only wired access-review connector without a name
resolver, so the source kept the generic "Notion" placeholder. Fetch
the workspace name from /v1/users/me (bot.workspace_name) following
the same pattern as the other resolvers, and refresh the stale scope
comment now that Notion participates in name resolution.
Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
The access-review source-name worker calls Customers.Get("my_customer")
on the Google Admin SDK to resolve the Google Workspace primary domain.
That endpoint requires admin.directory.customer.readonly; without it
the request returns 403 and the source keeps the generic placeholder
name. The scope is already requested by the SCIM bridge -- align the
access-review driver with it.
Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>