From bbdea575d1408646d220beda3a2328a89006816e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89mile=20R=C3=A9?= Date: Fri, 9 Jan 2026 10:31:19 +0100 Subject: [PATCH] Expire invitations on SAML sign up and invitation accept MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Émile Ré --- .../_components/NewSAMLConfigurationForm.tsx | 2 + pkg/coredata/invitation.go | 38 ++----------------- pkg/iam/account_service.go | 4 +- pkg/iam/saml/service.go | 6 +-- pkg/iam/scim/service.go | 12 +++--- 5 files changed, 17 insertions(+), 45 deletions(-) diff --git a/apps/console/src/pages/iam/organizations/settings/_components/NewSAMLConfigurationForm.tsx b/apps/console/src/pages/iam/organizations/settings/_components/NewSAMLConfigurationForm.tsx index 49def49cc..a2e6e414a 100644 --- a/apps/console/src/pages/iam/organizations/settings/_components/NewSAMLConfigurationForm.tsx +++ b/apps/console/src/pages/iam/organizations/settings/_components/NewSAMLConfigurationForm.tsx @@ -25,6 +25,8 @@ const createSAMLConfigurationMutation = graphql` domainVerificationToken domainVerifiedAt testLoginUrl + canUpdate: permission(action: "iam:saml-configuration:update") + canDelete: permission(action: "iam:saml-configuration:delete") } } } diff --git a/pkg/coredata/invitation.go b/pkg/coredata/invitation.go index 95f10fa18..18fecf319 100644 --- a/pkg/coredata/invitation.go +++ b/pkg/coredata/invitation.go @@ -164,39 +164,6 @@ WHERE return nil } -func (i *Invitations) AcceptByEmailAndOrganization( - ctx context.Context, - conn pg.Conn, - scope Scoper, - email mail.Addr, - organizationID gid.GID, - filter *InvitationFilter, -) error { - q := ` -UPDATE iam_invitations SET accepted_at = NOW() -WHERE - email = @email - AND organization_id = @organization_id - AND %s - AND %s -` - - q = fmt.Sprintf(q, scope.SQLFragment(), filter.SQLFragment()) - - args := pgx.StrictNamedArgs{ - "email": email, - "organization_id": organizationID, - } - maps.Copy(args, scope.SQLArguments()) - maps.Copy(args, filter.SQLArguments()) - - if _, err := conn.Exec(ctx, q, args); err != nil { - return fmt.Errorf("cannot accept invitations: %w", err) - } - - return nil -} - func (i *Invitations) ExpireByEmailAndOrganization( ctx context.Context, conn pg.Conn, @@ -206,7 +173,10 @@ func (i *Invitations) ExpireByEmailAndOrganization( filter *InvitationFilter, ) error { q := ` -UPDATE iam_invitations SET expires_at = NOW() +UPDATE + iam_invitations +SET + expires_at = NOW() WHERE email = @email AND organization_id = @organization_id diff --git a/pkg/iam/account_service.go b/pkg/iam/account_service.go index 045f7a35c..6a008fd7e 100644 --- a/pkg/iam/account_service.go +++ b/pkg/iam/account_service.go @@ -303,7 +303,7 @@ func (s *AccountService) AcceptInvitation( // Accept other pending invitations for email in organization invitations := &coredata.Invitations{} onlyPending := coredata.NewInvitationFilter([]coredata.InvitationStatus{coredata.InvitationStatusPending}) - if err := invitations.AcceptByEmailAndOrganization( + if err := invitations.ExpireByEmailAndOrganization( ctx, tx, coredata.NewScopeFromObjectID(invitation.OrganizationID), @@ -311,7 +311,7 @@ func (s *AccountService) AcceptInvitation( invitation.OrganizationID, onlyPending, ); err != nil { - return fmt.Errorf("cannot accept pending invitations by email: %w", err) + return fmt.Errorf("cannot expire pending invitations by email: %w", err) } return nil diff --git a/pkg/iam/saml/service.go b/pkg/iam/saml/service.go index 28b067d23..d7703443b 100644 --- a/pkg/iam/saml/service.go +++ b/pkg/iam/saml/service.go @@ -326,10 +326,10 @@ func (s *Service) HandleAssertion( return fmt.Errorf("cannot insert membership profile: %w", err) } - // Accept all pending invitations for email in organization + // Expire all pending invitations for email in organization invitations := &coredata.Invitations{} onlyPending := coredata.NewInvitationFilter([]coredata.InvitationStatus{coredata.InvitationStatusPending}) - if err := invitations.AcceptByEmailAndOrganization( + if err := invitations.ExpireByEmailAndOrganization( ctx, tx, coredata.NewScopeFromObjectID(config.OrganizationID), @@ -337,7 +337,7 @@ func (s *Service) HandleAssertion( config.OrganizationID, onlyPending, ); err != nil { - return fmt.Errorf("cannot accept pending invitations by email: %w", err) + return fmt.Errorf("cannot expire pending invitations by email: %w", err) } } diff --git a/pkg/iam/scim/service.go b/pkg/iam/scim/service.go index 081a26abc..26b312f3e 100644 --- a/pkg/iam/scim/service.go +++ b/pkg/iam/scim/service.go @@ -173,10 +173,10 @@ func (s *Service) CreateUser( return fmt.Errorf("cannot insert membership profile: %w", err) } - // Accept all pending invitations for email in organization + // Expire all pending invitations for email in organization invitations := &coredata.Invitations{} onlyPending := coredata.NewInvitationFilter([]coredata.InvitationStatus{coredata.InvitationStatusPending}) - err := invitations.AcceptByEmailAndOrganization( + err := invitations.ExpireByEmailAndOrganization( ctx, tx, coredata.NewScopeFromObjectID(config.OrganizationID), @@ -186,7 +186,7 @@ func (s *Service) CreateUser( ) if err != nil { - return fmt.Errorf("cannot accept pending invitations by email") + return fmt.Errorf("cannot expire pending invitations by email") } } else if err != nil { return fmt.Errorf("cannot load membership: %w", err) @@ -375,10 +375,10 @@ func (s *Service) updateUser( membership.Role = coredata.MembershipRoleEmployee needsUpdate = true - // Accept all pending invitations for email in organization + // Expire all pending invitations for email in organization invitations := &coredata.Invitations{} onlyPending := coredata.NewInvitationFilter([]coredata.InvitationStatus{coredata.InvitationStatusPending}) - if err := invitations.AcceptByEmailAndOrganization( + if err := invitations.ExpireByEmailAndOrganization( ctx, tx, coredata.NewScopeFromObjectID(config.OrganizationID), @@ -386,7 +386,7 @@ func (s *Service) updateUser( config.OrganizationID, onlyPending, ); err != nil { - return fmt.Errorf("cannot accept pending invitations by email: %w", err) + return fmt.Errorf("cannot expire pending invitations by email: %w", err) } } else if !*active && membership.State == coredata.MembershipStateActive { membership.State = coredata.MembershipStateInactive