Commit Graph

17 Commits

Author SHA1 Message Date
Sacha Al Himdani
86c45875a4 Whitelist ownership grants via allow policies
Replace the deny-based restriction on granting OWNER with role-scoped
allow policies so authorization fails closed: admins may create and
update memberships only when the assigned role is not OWNER, and the
absence of a target role no longer implies permission.

To keep console UI gating accurate without loosening the base grants,
the permission field gains an optional typed options argument
(PermissionOptionsInput) that forwards target_role into the dry-run
authorization. Only the two role-related console calls (create user,
update membership) pass it; the OWNER option stays hidden for admins via
the existing assignable-roles helper.

Add a non-regression test that an admin cannot promote a member to OWNER
while still being able to change members between non-owner roles.
2026-07-08 18:41:13 +02:00
Sacha Al Himdani
ff9cb881e8 Enforce owner-only member removal and ownership grants via policy
An organization ADMIN could hard-remove members, including OWNERs, because
removeUser (connect and MCP) only checked the weaker iam:membership-profile:delete
gate. Authorize the owner-only iam:membership:delete instead, and expose the
source attribute on MembershipProfile so the owner grant's non-SCIM condition
can match.

Consolidate ownership-grant authorization into policy for both createUser and
updateMembership: each resolver passes the requested role as a target_role
attribute and ADMIN is denied granting ownership via deny-create-owner /
deny-promote-owner. target_role is distinct from resource.role, which is the
target's current role and guards editing existing owners. With no callers left,
the iam:membership-role:set-owner action (grant and OAuth2 scope) is removed.

Also pass the authorized scope through to the RemoveUser/CreateUser services,
gate the console Remove action on iam:membership:delete, and add regression
tests plus a changelog entry.

Signed-off-by: Sacha Al Himdani <sacha@probo.com>
2026-07-08 17:59:45 +02:00
Sacha Al Himdani
36c9636168 Add blank line before assignment in user test
Satisfy wsl_v5 which requires whitespace between a var block and
the following assignment statement.

Signed-off-by: Sacha Al Himdani <sacha@probo.com>
2026-07-03 17:44:20 +02:00
Sacha Al Himdani
2ffeb7f3e8 Require set-owner authorization to create OWNER membership
An organization ADMIN could mint an OWNER membership via createUser, which
only gated iam:membership-profile:create and bypassed the owner-only
iam:membership-role:set-owner check that updateMembership already enforces.

Gate the requested role in both createUser entry points (connect resolver
and the MCP CreateUserTool) with an additional set-owner authorization when
the role is OWNER, mirroring updateMembership. Add a regression test that
locks the ADMIN/OWNER privilege boundary the fix relies on.

Signed-off-by: Sacha Al Himdani <sacha@probo.com>
2026-07-03 17:37:26 +02:00
Ludovic Vielle
888fa4d63a Return conflict when removing a referenced person
Deleting a profile still referenced elsewhere (for example as an
asset owner) surfaced an internal error. PostgreSQL reports ON DELETE
RESTRICT blocks as SQLSTATE 23001, not 23503; map both in profile
delete and propagate ErrProfileInUse through removeUser as CONFLICT.

Signed-off-by: Ludovic Vielle <ludovic@probo.com>
2026-06-30 14:11:46 +02:00
Sacha Al Himdani
9ac71f948f Update contact email to hello@probo.com
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
2026-06-09 16:45:23 +02:00
Cursor Agent
0136e34986 Satisfy whitespace lint in demotion test
Separate the owner membership variable from the range loop in the e2e regression to match the repository's whitespace lint rules.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>
2026-05-28 18:37:44 -07:00
Cursor Agent
93ec0d73da Fix last owner demotion regression
Use the Connect schema's profile connection in the e2e regression so the test can find the sole owner's membership. Adjust whitespace around the resolver error path to satisfy Go lint.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>
2026-05-28 18:37:44 -07:00
Cursor Agent
e9bcdc85f4 Reject last owner demotion
Return a conflict when membership role updates would demote the final active owner in an organization.

Add an end-to-end regression that verifies the mutation fails and leaves the owner role intact.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>
2026-05-28 18:37:44 -07:00
Bryan Frimin
65bfaa9f51 Cancel signature requests when a contract ends
When UpdateUser sets a contract end date that is already in the past,
the user can no longer fulfill outstanding signature requests. Delete
their still-pending requests as part of the same update so they stop
appearing as awaiting signatures.

Signed-off-by: Bryan Frimin <bryan@probo.com>
2026-05-28 18:12:40 -07:00
Cursor Agent
f7309c642a Cancel signature requests on deactivation
Disabling a profile now removes pending document signature requests tied to that signatory. This keeps inactive accounts from retaining signature work across manual, archived, and SCIM deactivation flows.

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

Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
2026-05-28 14:58:01 -07:00
Cursor Agent
1e08a23ddc Split user remove and archive actions
Restore RemoveUser as a hard delete operation and surface dependency\nconflicts with a dedicated IAM error.\n\nAdd a new ArchiveUser flow that deactivates profiles while keeping the\nmember in the organization, then expose both actions across Connect, MCP,\nCLI, n8n, console UI, and e2e coverage.

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

Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
2026-05-27 12:23:17 -07:00
Cursor Agent
b50bbc8d6a Archive manual users on remove
Switch remove-user behavior for manually managed profiles from hard\ndelete to archival by deactivating the profile. This matches the\nrequested SCIM-like lifecycle while avoiding dependency errors for\nlinked records such as signatures and assets.\n\nThe remove flow now updates profile state to INACTIVE, updates\nmembership timestamps, and emits a user-updated webhook event instead of\ndelete events. E2E coverage now asserts that remove keeps the profile\nand marks it inactive.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>
2026-05-27 12:23:07 -07:00
Émile Ré
9156d6a16a Add wsl linter and fix
Signed-off-by: Émile Ré <emile@probo.com>
2026-05-20 09:27:28 +04:00
Sacha Al Himdani
8c02c53315 Update copyright headers across all Go files
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
2026-03-25 17:38:32 +01:00
Bryan Frimin
d6b9280038 Fix owner deletion by qualifying ambiguous tenant_id column
The CountActiveOwnerByOrganizationID query joins iam_membership_profiles with
iam_memberships, but used an unqualified tenant_id = @tenant_id in the WHERE
clause. Since both tables have a tenant_id column, PostgreSQL raised an
"ambiguous column" error when deleting an owner. Fixed by prefixing with the
table alias (p.tenant_id) to match the pattern used in CountByOrganizationID.

Added TestUser_RemoveOwner e2e test to verify one owner can remove another.

Signed-off-by: Bryan Frimin <bryan@getprobo.com>
2026-03-19 12:14:19 +01:00
Émile Ré
ab5fe8e5e2 Fix e2e tests
Signed-off-by: Émile Ré <emile@getprobo.com>
2026-02-19 16:13:01 +04:00