Fix Cursor driver inactive account detection

Cursor's Admin API exposes two independent removal signals: the isRemoved
boolean and a role value of "removed". They are not always consistent —
a member can carry role "removed" while isRemoved is still false, a known
gap documented on the Cursor community forum.

Previously Active was derived from isRemoved alone, so a member with
role "removed" but isRemoved=false was incorrectly reported as active.
Now either signal is sufficient to mark the account inactive.

Add a cassette entry and test case covering the inconsistent state
(role "removed", isRemoved false) to prevent regression.

Signed-off-by: Bryan Frimin <bryan@probo.com>
This commit is contained in:
Bryan Frimin
2026-06-11 15:59:10 +02:00
parent 4b64e59da4
commit a542851e8c
3 changed files with 17 additions and 6 deletions

View File

@@ -86,10 +86,12 @@ func (d *CursorDriver) ListAccounts(ctx context.Context) ([]AccountRecord, error
continue
}
// isRemoved is Cursor's only account-status signal, so Active is
// Cursor exposes two removal signals that are not always
// consistent: the isRemoved soft-delete flag and a role of
// "removed". Either one marks the member inactive, so Active is
// always populated (never nil): a removed member is reported
// inactive rather than dropped, per the AccountRecord contract.
active := !m.IsRemoved
active := !m.IsRemoved && m.Role != "removed"
records = append(records, AccountRecord{
Email: m.Email,
@@ -98,8 +100,8 @@ func (d *CursorDriver) ListAccounts(ctx context.Context) ([]AccountRecord, error
Active: &active,
IsAdmin: cursorIsAdmin(m.Role),
MFAStatus: coredata.MFAStatusUnknown,
AuthMethod: coredata.AccessEntryAuthMethodUnknown,
AccountType: coredata.AccessEntryAccountTypeUser,
AuthMethod: coredata.AccessReviewEntryAuthMethodUnknown,
AccountType: coredata.AccessReviewEntryAccountTypeUser,
ExternalID: m.ID,
})
}

View File

@@ -35,7 +35,7 @@ func TestCursorDriver(t *testing.T) {
driver := NewCursorDriver(client)
records, err := driver.ListAccounts(context.Background())
require.NoError(t, err)
require.Len(t, records, 3)
require.Len(t, records, 4)
member := records[0]
assert.Equal(t, "jane@example.com", member.Email)
@@ -61,6 +61,15 @@ func TestCursorDriver(t *testing.T) {
assert.False(t, removed.IsAdmin)
require.NotNil(t, removed.Active)
assert.False(t, *removed.Active)
// Cursor's two removal signals are not always consistent: a member can
// carry role "removed" while isRemoved is still false. The role alone
// must mark the account inactive.
removedByRole := records[3]
assert.Equal(t, "Removed", removedByRole.Role)
assert.False(t, removedByRole.IsAdmin)
require.NotNil(t, removedByRole.Active)
assert.False(t, *removedByRole.Active)
}
func TestCursorRole(t *testing.T) {

View File

@@ -19,7 +19,7 @@ interactions:
proto_minor: 0
content_length: -1
uncompressed: true
body: '{"teamMembers":[{"id":"10000001","name":"Jane Doe","email":"jane@example.com","role":"member","isRemoved":false},{"id":"10000002","name":"Alex Martin","email":"alex@example.com","role":"owner","isRemoved":false},{"id":"10000003","name":"John Smith","email":"john@example.com","role":"removed","isRemoved":true}]}'
body: '{"teamMembers":[{"id":"10000001","name":"Jane Doe","email":"jane@example.com","role":"member","isRemoved":false},{"id":"10000002","name":"Alex Martin","email":"alex@example.com","role":"owner","isRemoved":false},{"id":"10000003","name":"John Smith","email":"john@example.com","role":"removed","isRemoved":true},{"id":"10000004","name":"Mary Jones","email":"mary@example.com","role":"removed","isRemoved":false}]}'
headers:
Content-Type:
- application/json