Commit Graph

5415 Commits

Author SHA1 Message Date
Cursor Agent
fe215efbd1 Add audit start and end dates to audits
ISO audits often span a window distinct from certificate validity.
Store optional audit_start_date and audit_end_date on the audit
record and expose them through GraphQL, MCP, CLI, n8n, and console.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
2026-07-28 17:27:33 +02:00
Cursor Agent
dcb7ffa945 Suppress Mermaid render error artifacts
Configure Mermaid to suppress its built-in error renderer in both
interactive UI previews and generated document HTML. Render failures now
surface through the product toast flow with a safe inline fallback instead
of leaving raw Mermaid diagnostics in the document body.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Émile Ré <nemile.re@gmail.com>
2026-07-28 16:04:07 +02:00
Émile Ré
6e11886365 Extract magic link handler and verify email
Move MagicLinkHandler out of oidc_handler.go. Completing a
magic link for an existing identity now marks the address
verified, matching OIDC behavior.

Signed-off-by: Émile Ré <emile@probo.com>
2026-07-28 15:11:06 +02:00
Émile Ré
d08e4c3314 Expect verified email in OAuth2 ID token e2e
E2E owners confirm email during setup so password re-login works,
so ID token claim assertions must expect email_verified true.

Signed-off-by: Émile Ré <emile@probo.com>
2026-07-28 15:11:06 +02:00
Émile Ré
b64f101ab9 Drop resend verification email cooldown
Resend only runs after an explicit form submit, so a per-address
cooldown is unnecessary overhead compared with forgot-password.

Signed-off-by: Émile Ré <emile@probo.com>
2026-07-28 15:11:06 +02:00
Émile Ré
82a62005f8 Harden email verification resend against abuse
Add a per-address confirmation-email cooldown and disable the
resend/forgot-password submit buttons while the mutation is in
flight so callers cannot flood the mail queue or double-submit.

Signed-off-by: Émile Ré <emile@probo.com>
2026-07-28 15:11:06 +02:00
Émile Ré
5d0882778f Gate password sign-in on email verification
Unverified password identities were able to open sessions after
signing out. Reject sign-in with EMAIL_NOT_VERIFIED and add a
resend-confirmation flow so users can complete verification.

Signed-off-by: Émile Ré <emile@probo.com>
2026-07-28 15:11:05 +02:00
Émile Ré
bb9fb22913 Release @probo/cookie-banner/v0.10.1
Signed-off-by: Émile Ré <emile@getprobo.com>
2026-07-28 15:07:11 +02:00
Émile Ré
85bdcffdbb Allow integrations in discovery mode
Signed-off-by: Émile Ré <emile@probo.com>
2026-07-28 14:23:23 +02:00
Émile Ré
319b3ec7fb Add missing generated comments and llm records
Signed-off-by: Émile Ré <emile@probo.com>
2026-07-28 10:31:49 +02:00
Bryan Frimin
bcf244c787 Release @probo/n8n-nodes-probo/v0.207.0 2026-07-27 19:01:56 +02:00
Bryan Frimin
bc64e5ead8 Release probod/v0.237.0 2026-07-27 19:01:37 +02:00
Bryan Frimin
e94d224261 Release prb/v0.204.0 2026-07-27 19:01:17 +02:00
Bryan Frimin
636dd172e8 Fix wsl_v5 spacing in MergeByCampaignID
Add blank lines around the dedup-and-connector-ID block to satisfy
the wsl_v5 linter (blank line after an if-block, above an assignment
following a block, and above an if with no shared variables in the
preceding line).

Signed-off-by: Bryan Frimin <bryan@probo.com>
2026-07-27 18:56:25 +02:00
Bryan Frimin
b43e66ffa4 Remove restated doc comment on MergeByCampaignID
The comment repeated what the function signature and body already
say plainly.

Signed-off-by: Bryan Frimin <bryan@probo.com>
2026-07-27 18:56:25 +02:00
Bryan Frimin
68a64647bf Batch-load and validate campaign sources before merging
MergeByCampaignID joined access_review_sources directly from coredata
to resolve live sources, so unrecognized or out-of-scope IDs were
silently dropped instead of erroring, and in the worst case (every ID
invalid) the NOT MATCHED BY SOURCE clause deleted every existing
campaign source. The syncCampaignSources ErrResourceNotFound check
was therefore unreachable dead code.

Add AccessReviewSources.LoadByIDs, matching the existing scoped
LoadByIDs pattern (id = ANY(@ids) plus a resolved-count check), and
have CreateCampaign, AddCampaignSource, and syncCampaignSources
resolve and validate sources up front. MergeByCampaignID now takes
the already-loaded sources and builds its desired-state CTE from an
unnest() of their values instead of joining access_review_sources,
keeping the merge inside the campaign-source entity boundary.

Signed-off-by: Bryan Frimin <bryan@probo.com>
2026-07-27 18:56:24 +02:00
Bryan Frimin
8bc394c54b Paginate access review source loader in n8n node
The loader fetched only the first 500 sources with no pageInfo/cursor
follow-up, so organizations with more than 500 scoped sources could
not select sources past the first page when creating or updating a
campaign. Loop over pages using pageInfo.endCursor until exhausted.

Signed-off-by: Bryan Frimin <bryan@probo.com>
2026-07-27 18:56:24 +02:00
Sacha Al Himdani
2ebf3c180f Fix crash on third-party details when country is GLOBAL
Intl.DisplayNames rejects the GLOBAL pseudo-region, so label
resolution now handles it explicitly before rendering the picker.

Signed-off-by: Sacha Al Himdani <sacha@probo.com>
2026-07-27 18:56:24 +02:00
Émile Ré
302175617f Drop report org checks and noisy comments
Report access loads are already tenant-scoped; reject the
extra organization guard. Also remove the PortalAccessRequest
doc comment called out in review.

Signed-off-by: Émile Ré <emile@probo.com>
2026-07-27 18:27:21 +02:00
Émile Ré
14c9c921cc Drop unreachable no-targets error handlers
The single-resource access resolvers each forward exactly one hard-coded
id, so RequestPortalAccess never returns ErrNoAccessTargets on those
paths — it only does so when all three id slices are empty. Remove the
three dead handlers, keeping the live one on the bulk path where the
resolved id slices can legitimately all be empty.

Signed-off-by: Émile Ré <emile@probo.com>
2026-07-27 18:27:21 +02:00
Émile Ré
1e79755121 Derive selection count from live locked rows
The bar read the raw selection size for its count, so after a request
succeeded and Relay flipped the rows to pending the left count stayed
stale while the action count dropped to zero. Resolve the selection
against the current rows instead, so requested rows leave the count at
once and the bar closes without depending on a clear that may not run.

Signed-off-by: Émile Ré <emile@probo.com>
2026-07-27 18:27:21 +02:00
Émile Ré
d0c9327e99 Require explicit portal access request IDs
Drop the request-all shortcut so callers always name the
documents, reports, and files to request. TopBar Get Access
now only signs in; bulk selection is the multi-resource path.

Signed-off-by: Émile Ré <emile@probo.com>
2026-07-27 18:27:21 +02:00
Émile Ré
baa5a3588a Lock only document rows without an access record
The bulk request skips any id that already has an access record, so
rejected or revoked rows reported success while creating nothing. Treat a
row as requestable only when the viewer is unauthorized and no access
record exists yet, keeping the selection CTA count honest.

Signed-off-by: Émile Ré <emile@probo.com>
2026-07-27 18:27:21 +02:00
Émile Ré
dbdd79e1c1 Fix lint issues
Signed-off-by: Émile Ré <emile@probo.com>
2026-07-27 18:27:21 +02:00
Émile Ré
ca5c622a01 Stop empty toast viewport from eating pointer events
The toast viewport stays mounted even with no toasts, as a fixed box in
the bottom-right corner. With default pointer-events it swallowed hover
and clicks over content beneath it, such as the lower edge of a bottom
action bar's right-aligned buttons.

Mark the viewport pointer-events-none and re-enable events on each toast
root so only actual toast cards are interactive.

Signed-off-by: Émile Ré <emile@probo.com>
2026-07-27 18:27:21 +02:00
Émile Ré
d2638a3d78 Extract selection bar styles into a tailwind variant
The bottom selection action bar carried its layout as inline class
strings on the container divs. Move them into a slotted tv in the
documents variants module, matching the documentsLayout pattern, so the
component consumes named slots instead of ad hoc className literals.

Signed-off-by: Émile Ré <emile@probo.com>
2026-07-27 18:27:20 +02:00
Émile Ré
1d6218cb7d Disable row selection for non-lockable documents
Public / already-authorized and already-requested rows could be ticked
even though they never contribute to "Request Access (N)", making the
selection count look inconsistent with the CTA. Restrict selection to
locked rows: their checkbox is disabled otherwise, and "Select all" now
picks only the lockable rows.

Signed-off-by: Émile Ré <emile@probo.com>
2026-07-27 18:27:20 +02:00
Émile Ré
9353d85d03 Add bulk request access to portal documents
Visitors could only request access to one document, report, or file at
a time. Add row checkboxes and a bottom selection toolbar to the
compliance portal documents page so a visitor can select several rows
and request access to all still-locked ones in a single round-trip.

Expose a selection-scoped requestAccesses mutation that forwards the
chosen id lists to the existing RequestPortalAccess service (one
transaction, one NDA/auth gate). The resolver loads and tenant-checks
every target before requesting so a foreign id is rejected before any
access row is written, and echoes the affected nodes so the client
flips each row to pending in place.

Add a styled Base UI Checkbox to the v2 kit, a local selection context
shared by the independent row fragments, and mirror the new selection
strings across all locales.

Signed-off-by: Émile Ré <emile@probo.com>
2026-07-27 18:27:20 +02:00
Émile Ré
15b51640d3 Fix geoloc country ip block import script
Signed-off-by: Émile Ré <emile@probo.com>
2026-07-27 18:21:57 +02:00
Émile Ré
10200cb4c0 Update country ip blocks
Signed-off-by: Émile Ré <emile@probo.com>
2026-07-27 18:17:00 +02:00
Cursor Agent
3455784f52 Remove unused uniqueGIDs helper
MergeByCampaignID already dedupes with gid.NewSet.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
2026-07-27 15:56:37 +00:00
Cursor Agent
9ad52fc27a Fix undefined source in AddCampaignSource
Declare AccessReviewSource before LoadByID so the package compiles
after RemoveCampaignSource cleanup.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
2026-07-27 15:56:37 +00:00
Cursor Agent
b5574764cf Dedupe campaign source IDs with gid.NewSet
Remove local uniqueGIDs helper in favor of the existing gid set
utility used elsewhere in coredata.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
2026-07-27 15:56:37 +00:00
Cursor Agent
3b3d7002a9 Drop pre-merge access source count check
Scoped rows in the MERGE desired_sources CTE already limit
snapshots to sources visible under the scoper.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
2026-07-27 15:56:37 +00:00
Cursor Agent
4d154651a8 Use one path for campaign source merge
Remove early return on empty source IDs; count and MERGE always
run so clearing sources uses NOT MATCHED BY SOURCE delete.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
2026-07-27 15:56:37 +00:00
Cursor Agent
447bdb88e5 Early-return empty campaign source merge
Delete scoped snapshots in one statement when the desired source
set is empty; validate and merge only when IDs are present.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
2026-07-27 15:56:37 +00:00
Cursor Agent
abbb1ee875 Merge campaign scope sources in one SQL statement
Add AccessReviewCampaignSources.MergeByCampaignID and use it from
syncCampaignSources instead of per-row load, upsert, and delete.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
2026-07-27 15:56:36 +00:00
Cursor Agent
f396107090 Drop redundant access source organization checks
Scoped LoadByID already enforces tenant isolation; remove
OrganizationID comparisons against the campaign in create, sync,
add source, and review engine paths.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
2026-07-27 15:56:36 +00:00
Cursor Agent
6fc3b1e3e3 Remove accessreview Campaign wrapper type
Apply draft and lifecycle checks on loaded coredata records
directly in the campaign service.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
2026-07-27 15:56:36 +00:00
Cursor Agent
3f545652d5 Use business campaign rules instead of status errors
Introduce accessreview.Campaign with draft/deletable predicates and
operation-specific client errors. Drop errUnlessDraftCampaign and
status-to-sentinel switches in the service layer.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
2026-07-27 15:56:07 +00:00
Cursor Agent
5dca6bb883 Use struct campaign errors with campaign ID in message
Business-layer errors carry the access review campaign GID in
Error() and match GraphQL sentinels via Is().

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
2026-07-27 15:56:07 +00:00
Cursor Agent
1422b6d5dd Express campaign client errors in the service layer
Remove status-to-error helpers and map loaded campaign data to
accessreview sentinels at each business rule instead of wrapping
coredata status values.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
2026-07-27 15:56:07 +00:00
Cursor Agent
09f131087c Map cancel campaign terminal errors to invalid
Return GraphQL invalid when cancel hits completed or cancelled
campaign status sentinels from the service layer.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
2026-07-27 15:56:07 +00:00
Cursor Agent
352c551e53 Return campaign status sentinels without wrapping
Drop CampaignStatusError from errors.go and map coredata status to
client errors in the service layer only. Cancel on terminal statuses
uses the same sentinels instead of fmt status strings.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
2026-07-27 15:56:07 +00:00
Cursor Agent
c187ea7124 Remove CampaignStatusErrors slice
List campaign status sentinels explicitly at resolver AnyOf
call sites instead of a shared slice.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
2026-07-27 15:56:07 +00:00
Cursor Agent
f8dcb6c014 Add errorx.AnyOf for sentinel error checks
Centralize errors.Is OR chains in access review campaign resolvers
and expose CampaignStatusErrors for the four non-draft status
sentinels.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
2026-07-27 15:56:07 +00:00
Cursor Agent
b3e60da4d5 Use status-specific errors for non-draft campaigns
Replace ErrCampaignCannotStart and ErrCampaignCannotUpdate with
sentinels per campaign status, mapped through CampaignStatusError
and wrapped with operation-specific fmt.Errorf prefixes.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
2026-07-27 15:56:07 +00:00
Cursor Agent
3d606dd0e8 Replace ErrCampaignNotDraft with operation-specific errors
Use ErrCampaignCannotStart and ErrCampaignCannotUpdate with clear
actionable messages instead of a generic draft-status sentinel
wrapped in noisy fmt.Errorf chains.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
2026-07-27 15:56:07 +00:00
Cursor Agent
eabd0fb659 Rename ErrCampaignNoSourcesSelected to ErrCampaignMissingSources
The failure is about unconfigured campaign sources, not an empty UI
selection.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
2026-07-27 15:56:07 +00:00
Cursor Agent
eebc957d36 Treat invalid campaign sources as not found
Drop ErrCampaignSourceOrganizationMismatch. When a source ID is
missing or belongs to another organization, return
coredata.ErrResourceNotFound so clients get a generic not-found
response instead of leaking cross-organization details.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
2026-07-27 15:56:07 +00:00