From 436226e17ae3c5c2ddf13f55aea4203cb02e13a3 Mon Sep 17 00:00:00 2001 From: "c1-dev-bot[bot]" <2740113+c1-dev-bot[bot]@users.noreply.github.com> Date: Mon, 11 May 2026 17:32:57 +0000 Subject: [PATCH] Expose invitation timestamps in org invitation resources Add created_at, expires_at (created_at + 7 days), failed_at, failed_reason, and role fields to the invitation user profile. Also set the CreatedAt trait on the user resource when available. This enables customers to identify expired pending SCIM invitations and clean up seats occupied by stale invites. Fixes: CXH-1455 --- pkg/connector/invitation.go | 44 +++++++++++++++++++++++++++++-------- 1 file changed, 35 insertions(+), 9 deletions(-) diff --git a/pkg/connector/invitation.go b/pkg/connector/invitation.go index c74f6d38..af479a05 100644 --- a/pkg/connector/invitation.go +++ b/pkg/connector/invitation.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "strconv" + "time" v2 "github.com/conductorone/baton-sdk/pb/c1/connector/v2" "github.com/conductorone/baton-sdk/pkg/annotations" @@ -12,25 +13,50 @@ import ( "github.com/google/go-github/v69/github" ) +const githubInvitationExpiryDuration = 7 * 24 * time.Hour + func invitationToUserResource(invitation *github.Invitation) (*v2.Resource, error) { login := invitation.GetLogin() if login == "" { login = invitation.GetEmail() } + profile := map[string]interface{}{ + "login": login, + "inviter": invitation.GetInviter().GetLogin(), + "role": invitation.GetRole(), + } + + createdAt := invitation.GetCreatedAt() + if !createdAt.IsZero() { + profile["created_at"] = createdAt.Time.Format(time.RFC3339) + expiresAt := createdAt.Add(githubInvitationExpiryDuration) + profile["expires_at"] = expiresAt.Format(time.RFC3339) + } + + failedAt := invitation.GetFailedAt() + if !failedAt.IsZero() { + profile["failed_at"] = failedAt.Time.Format(time.RFC3339) + } + if reason := invitation.GetFailedReason(); reason != "" { + profile["failed_reason"] = reason + } + + userTraitOpts := []resourceSdk.UserTraitOption{ + resourceSdk.WithEmail(invitation.GetEmail(), true), + resourceSdk.WithUserProfile(profile), + resourceSdk.WithStatus(v2.UserTrait_Status_STATUS_UNSPECIFIED), + resourceSdk.WithUserLogin(login), + } + if !createdAt.IsZero() { + userTraitOpts = append(userTraitOpts, resourceSdk.WithCreatedAt(createdAt.Time)) + } + ret, err := resourceSdk.NewUserResource( login, resourceTypeInvitation, invitation.GetID(), - []resourceSdk.UserTraitOption{ - resourceSdk.WithEmail(invitation.GetEmail(), true), - resourceSdk.WithUserProfile(map[string]interface{}{ - "login": login, - "inviter": invitation.GetInviter().GetLogin(), - }), - resourceSdk.WithStatus(v2.UserTrait_Status_STATUS_UNSPECIFIED), - resourceSdk.WithUserLogin(login), - }, + userTraitOpts, ) if err != nil { return nil, err