Split inactive profile state

Replace the binary profile ACTIVE/INACTIVE model with PENDING, ACTIVE,
and DEACTIVATED so invited-but-not-yet-activated members remain
assignable to assets, data, and risks instead of being treated like
deactivated users.

Add activated_at/deactivated_at timestamps and Mark* lifecycle helpers,
and update every transition (create, invite/re-invite, activation,
archive, SCIM, SAML, sessions, compliance-portal grant) to the new
states. Expose a multi-state states[] filter across coredata, GraphQL,
MCP, and the console owner pickers, which now request ACTIVE and
PENDING members.

A migration renames the membership_state enum, classifies existing
inactive profiles as PENDING from recent invitation activity, and
backfills the new timestamp columns.

Signed-off-by: Émile Ré <emile@probo.com>
This commit is contained in:
Émile Ré
2026-07-28 17:03:52 +02:00
parent 6b3913b189
commit 4a276e3ef7
29 changed files with 261 additions and 64 deletions

View File

@@ -9,9 +9,10 @@ type OrganizationContext {
enum ProfileState
@goModel(model: "go.probo.inc/probo/pkg/coredata.ProfileState") {
PENDING @goEnum(value: "go.probo.inc/probo/pkg/coredata.ProfileStatePending")
ACTIVE @goEnum(value: "go.probo.inc/probo/pkg/coredata.ProfileStateActive")
INACTIVE
@goEnum(value: "go.probo.inc/probo/pkg/coredata.ProfileStateInactive")
DEACTIVATED
@goEnum(value: "go.probo.inc/probo/pkg/coredata.ProfileStateDeactivated")
}
enum MembershipRole
@@ -69,6 +70,7 @@ input ProfileOrder
input ProfileFilter {
contractEnded: Boolean
state: ProfileState
states: [ProfileState!]
query: String
role: MembershipRole
kind: String

View File

@@ -119,6 +119,10 @@ func (r *organizationResolver) Profiles(ctx context.Context, obj *types.Organiza
if filter != nil {
filters = coredata.NewMembershipProfileFilter(filter.ContractEnded).WithMembership()
if len(filter.States) > 0 {
filters.WithStates(filter.States...)
}
if filter.State != nil {
filters.WithState(*filter.State)
}