Include all accounts in access-review fetch
Drivers whose source API surfaces an account-status signal return every account (including inactive / suspended / deleted) and populate Active accordingly; drivers without such a signal no longer fabricate it. The fetch pipeline records every account with decision PENDING and no flags. Producing flags or a non-PENDING decision is reserved to human reviewers or a purpose-built agent run against the campaign -- the engine must not form an implicit verdict. Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
This commit is contained in:
@@ -68,7 +68,7 @@ func (d *BrexDriver) ListAccounts(ctx context.Context) ([]AccountRecord, error)
|
|||||||
Email: u.Email,
|
Email: u.Email,
|
||||||
FullName: u.FirstName + " " + u.LastName,
|
FullName: u.FirstName + " " + u.LastName,
|
||||||
Role: u.Role,
|
Role: u.Role,
|
||||||
Active: u.Status == "ACTIVE",
|
Active: new(u.Status == "ACTIVE"),
|
||||||
IsAdmin: false,
|
IsAdmin: false,
|
||||||
ExternalID: u.ID,
|
ExternalID: u.ID,
|
||||||
MFAStatus: coredata.MFAStatusUnknown,
|
MFAStatus: coredata.MFAStatusUnknown,
|
||||||
|
|||||||
@@ -184,7 +184,7 @@ func (d *CloudflareDriver) queryAllMembers(ctx context.Context, accountID string
|
|||||||
Email: m.User.Email,
|
Email: m.User.Email,
|
||||||
FullName: m.User.FirstName + " " + m.User.LastName,
|
FullName: m.User.FirstName + " " + m.User.LastName,
|
||||||
Role: role,
|
Role: role,
|
||||||
Active: m.Status == "accepted",
|
Active: new(m.Status == "accepted"),
|
||||||
IsAdmin: isAdmin,
|
IsAdmin: isAdmin,
|
||||||
ExternalID: m.ID,
|
ExternalID: m.ID,
|
||||||
MFAStatus: mfaStatus,
|
MFAStatus: mfaStatus,
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ func (d *CSVDriver) ListAccounts(_ context.Context) ([]AccountRecord, error) {
|
|||||||
record.IsAdmin = strings.TrimSpace(strings.ToLower(row[idx])) == "true"
|
record.IsAdmin = strings.TrimSpace(strings.ToLower(row[idx])) == "true"
|
||||||
}
|
}
|
||||||
if idx, ok := colIndex["active"]; ok && idx < len(row) {
|
if idx, ok := colIndex["active"]; ok && idx < len(row) {
|
||||||
record.Active = strings.TrimSpace(strings.ToLower(row[idx])) == "true"
|
record.Active = new(strings.TrimSpace(strings.ToLower(row[idx])) == "true")
|
||||||
}
|
}
|
||||||
if idx, ok := colIndex["external_id"]; ok && idx < len(row) {
|
if idx, ok := colIndex["external_id"]; ok && idx < len(row) {
|
||||||
record.ExternalID = strings.TrimSpace(row[idx])
|
record.ExternalID = strings.TrimSpace(row[idx])
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ func (d *DocuSignDriver) ListAccounts(ctx context.Context) ([]AccountRecord, err
|
|||||||
FullName: u.UserName,
|
FullName: u.UserName,
|
||||||
Role: u.PermissionProfileName,
|
Role: u.PermissionProfileName,
|
||||||
JobTitle: u.JobTitle,
|
JobTitle: u.JobTitle,
|
||||||
Active: strings.EqualFold(u.UserStatus, "active"),
|
Active: new(strings.EqualFold(u.UserStatus, "active")),
|
||||||
IsAdmin: strings.EqualFold(u.IsAdmin, "True"),
|
IsAdmin: strings.EqualFold(u.IsAdmin, "True"),
|
||||||
ExternalID: u.UserID,
|
ExternalID: u.UserID,
|
||||||
MFAStatus: coredata.MFAStatusUnknown,
|
MFAStatus: coredata.MFAStatusUnknown,
|
||||||
|
|||||||
@@ -23,13 +23,23 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// AccountRecord represents a single account from an access source or identity
|
// AccountRecord represents a single account from an access source or identity
|
||||||
// source. All fields are best-effort; sources populate what they can.
|
// source. All fields are best-effort; sources populate what they can. Drivers
|
||||||
|
// must return ALL accounts the source exposes (including inactive / suspended
|
||||||
|
// / deleted); classification is the job of the reviewer or of an agent run
|
||||||
|
// against the campaign, not of the fetch pipeline.
|
||||||
|
//
|
||||||
|
// Active is three-valued: nil means the source API has no explicit
|
||||||
|
// account-status signal for this account (the driver cannot tell), a non-nil
|
||||||
|
// pointer means the driver observed an explicit signal (true = active at
|
||||||
|
// source, false = deactivated / suspended / deleted). Drivers whose API does
|
||||||
|
// not distinguish active from deactivated accounts must leave Active nil
|
||||||
|
// rather than fabricate a value.
|
||||||
type AccountRecord struct {
|
type AccountRecord struct {
|
||||||
Email string
|
Email string
|
||||||
FullName string
|
FullName string
|
||||||
Role string // system role/permission (e.g. "Admin", "Viewer")
|
Role string // system role/permission (e.g. "Admin", "Viewer")
|
||||||
JobTitle string // HR job title / department (e.g. "Software Engineer")
|
JobTitle string // HR job title / department (e.g. "Software Engineer")
|
||||||
Active bool
|
Active *bool
|
||||||
IsAdmin bool
|
IsAdmin bool
|
||||||
MFAStatus coredata.MFAStatus
|
MFAStatus coredata.MFAStatus
|
||||||
AuthMethod coredata.AccessEntryAuthMethod
|
AuthMethod coredata.AccessEntryAuthMethod
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ func (d *GitHubDriver) ListAccounts(ctx context.Context) ([]AccountRecord, error
|
|||||||
Email: profile.Email,
|
Email: profile.Email,
|
||||||
FullName: fullName,
|
FullName: fullName,
|
||||||
Role: membership.Role,
|
Role: membership.Role,
|
||||||
Active: membership.State == "active",
|
Active: new(membership.State == "active"),
|
||||||
IsAdmin: membership.Role == "admin",
|
IsAdmin: membership.Role == "admin",
|
||||||
MFAStatus: mfaStatus,
|
MFAStatus: mfaStatus,
|
||||||
AuthMethod: coredata.AccessEntryAuthMethodUnknown,
|
AuthMethod: coredata.AccessEntryAuthMethodUnknown,
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ func (d *GoogleWorkspaceDriver) ListAccounts(ctx context.Context) ([]AccountReco
|
|||||||
rec := AccountRecord{
|
rec := AccountRecord{
|
||||||
Email: u.PrimaryEmail,
|
Email: u.PrimaryEmail,
|
||||||
FullName: u.Name.FullName,
|
FullName: u.Name.FullName,
|
||||||
Active: !u.Suspended && !u.Archived,
|
Active: new(!u.Suspended && !u.Archived),
|
||||||
IsAdmin: u.IsAdmin,
|
IsAdmin: u.IsAdmin,
|
||||||
ExternalID: u.Id,
|
ExternalID: u.Id,
|
||||||
MFAStatus: coredata.MFAStatusUnknown,
|
MFAStatus: coredata.MFAStatusUnknown,
|
||||||
|
|||||||
@@ -99,7 +99,6 @@ func (d *HubSpotDriver) ListAccounts(ctx context.Context) ([]AccountRecord, erro
|
|||||||
Email: u.Email,
|
Email: u.Email,
|
||||||
FullName: fullName,
|
FullName: fullName,
|
||||||
Role: role,
|
Role: role,
|
||||||
Active: true,
|
|
||||||
IsAdmin: u.SuperAdmin,
|
IsAdmin: u.SuperAdmin,
|
||||||
ExternalID: u.ID,
|
ExternalID: u.ID,
|
||||||
MFAStatus: coredata.MFAStatusUnknown,
|
MFAStatus: coredata.MFAStatusUnknown,
|
||||||
|
|||||||
@@ -67,7 +67,6 @@ func (d *IntercomDriver) ListAccounts(ctx context.Context) ([]AccountRecord, err
|
|||||||
FullName: a.Name,
|
FullName: a.Name,
|
||||||
Role: intercomRole(a.HasInboxSeat),
|
Role: intercomRole(a.HasInboxSeat),
|
||||||
JobTitle: a.JobTitle,
|
JobTitle: a.JobTitle,
|
||||||
Active: true,
|
|
||||||
IsAdmin: false, // Intercom API does not expose admin role information
|
IsAdmin: false, // Intercom API does not expose admin role information
|
||||||
ExternalID: a.ID,
|
ExternalID: a.ID,
|
||||||
MFAStatus: coredata.MFAStatusUnknown,
|
MFAStatus: coredata.MFAStatusUnknown,
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ func (d *LinearDriver) ListAccounts(ctx context.Context) ([]AccountRecord, error
|
|||||||
Email: u.Email,
|
Email: u.Email,
|
||||||
FullName: u.Name,
|
FullName: u.Name,
|
||||||
Role: linearRole(u.Admin, u.Guest),
|
Role: linearRole(u.Admin, u.Guest),
|
||||||
Active: u.Active,
|
Active: new(u.Active),
|
||||||
IsAdmin: u.Admin,
|
IsAdmin: u.Admin,
|
||||||
ExternalID: u.ID,
|
ExternalID: u.ID,
|
||||||
MFAStatus: coredata.MFAStatusUnknown,
|
MFAStatus: coredata.MFAStatusUnknown,
|
||||||
|
|||||||
@@ -81,7 +81,6 @@ func (d *NotionDriver) ListAccounts(ctx context.Context) ([]AccountRecord, error
|
|||||||
Email: email,
|
Email: email,
|
||||||
FullName: u.Name,
|
FullName: u.Name,
|
||||||
Role: "Member",
|
Role: "Member",
|
||||||
Active: true,
|
|
||||||
IsAdmin: false,
|
IsAdmin: false,
|
||||||
ExternalID: u.ID,
|
ExternalID: u.ID,
|
||||||
MFAStatus: coredata.MFAStatusUnknown,
|
MFAStatus: coredata.MFAStatusUnknown,
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ func (d *OnePasswordDriver) ListAccounts(ctx context.Context) ([]AccountRecord,
|
|||||||
record := AccountRecord{
|
record := AccountRecord{
|
||||||
Email: email,
|
Email: email,
|
||||||
FullName: u.DisplayName,
|
FullName: u.DisplayName,
|
||||||
Active: u.Active,
|
Active: new(u.Active),
|
||||||
ExternalID: u.ID,
|
ExternalID: u.ID,
|
||||||
MFAStatus: coredata.MFAStatusUnknown,
|
MFAStatus: coredata.MFAStatusUnknown,
|
||||||
AuthMethod: coredata.AccessEntryAuthMethodUnknown,
|
AuthMethod: coredata.AccessEntryAuthMethodUnknown,
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ func (d *OnePasswordUsersAPIDriver) ListAccounts(ctx context.Context) ([]Account
|
|||||||
record := AccountRecord{
|
record := AccountRecord{
|
||||||
Email: u.Email,
|
Email: u.Email,
|
||||||
FullName: u.DisplayName,
|
FullName: u.DisplayName,
|
||||||
Active: u.State == "ACTIVE",
|
Active: new(u.State == "ACTIVE"),
|
||||||
ExternalID: u.ID,
|
ExternalID: u.ID,
|
||||||
MFAStatus: coredata.MFAStatusUnknown,
|
MFAStatus: coredata.MFAStatusUnknown,
|
||||||
AuthMethod: coredata.AccessEntryAuthMethodUnknown,
|
AuthMethod: coredata.AccessEntryAuthMethodUnknown,
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ func (d *OpenAIDriver) ListAccounts(ctx context.Context) ([]AccountRecord, error
|
|||||||
Email: u.Email,
|
Email: u.Email,
|
||||||
FullName: u.Name,
|
FullName: u.Name,
|
||||||
Role: openaiRole(u.Role),
|
Role: openaiRole(u.Role),
|
||||||
Active: !u.Disabled,
|
Active: new(!u.Disabled),
|
||||||
IsAdmin: u.Role == "owner",
|
IsAdmin: u.Role == "owner",
|
||||||
ExternalID: u.ID,
|
ExternalID: u.ID,
|
||||||
MFAStatus: coredata.MFAStatusUnknown,
|
MFAStatus: coredata.MFAStatusUnknown,
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ func (d *ProboMembershipsDriver) ListAccounts(ctx context.Context) ([]AccountRec
|
|||||||
Email: account.Email,
|
Email: account.Email,
|
||||||
FullName: account.FullName,
|
FullName: account.FullName,
|
||||||
Role: role,
|
Role: role,
|
||||||
Active: account.State == string(coredata.ProfileStateActive),
|
Active: new(account.State == string(coredata.ProfileStateActive)),
|
||||||
IsAdmin: isAdmin,
|
IsAdmin: isAdmin,
|
||||||
ExternalID: account.ID.String(),
|
ExternalID: account.ID.String(),
|
||||||
CreatedAt: &createdAt,
|
CreatedAt: &createdAt,
|
||||||
|
|||||||
@@ -57,7 +57,6 @@ func (d *ResendDriver) ListAccounts(ctx context.Context) ([]AccountRecord, error
|
|||||||
for _, k := range resp.Data {
|
for _, k := range resp.Data {
|
||||||
record := AccountRecord{
|
record := AccountRecord{
|
||||||
FullName: k.Name,
|
FullName: k.Name,
|
||||||
Active: true,
|
|
||||||
IsAdmin: false,
|
IsAdmin: false,
|
||||||
ExternalID: k.ID,
|
ExternalID: k.ID,
|
||||||
MFAStatus: coredata.MFAStatusUnknown,
|
MFAStatus: coredata.MFAStatusUnknown,
|
||||||
|
|||||||
@@ -144,7 +144,7 @@ func (d *SentryDriver) ListAccounts(ctx context.Context) ([]AccountRecord, error
|
|||||||
Email: m.Email,
|
Email: m.Email,
|
||||||
FullName: fullName,
|
FullName: fullName,
|
||||||
Role: m.OrgRole,
|
Role: m.OrgRole,
|
||||||
Active: active,
|
Active: new(active),
|
||||||
IsAdmin: isAdmin,
|
IsAdmin: isAdmin,
|
||||||
ExternalID: m.ID,
|
ExternalID: m.ID,
|
||||||
MFAStatus: mfaStatus,
|
MFAStatus: mfaStatus,
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ func (d *SlackDriver) ListAccounts(ctx context.Context) ([]AccountRecord, error)
|
|||||||
FullName: m.RealName,
|
FullName: m.RealName,
|
||||||
JobTitle: m.Profile.Title,
|
JobTitle: m.Profile.Title,
|
||||||
Role: slackRole(m),
|
Role: slackRole(m),
|
||||||
Active: !m.Deleted,
|
Active: new(!m.Deleted),
|
||||||
IsAdmin: m.IsAdmin || m.IsOwner || m.IsPrimaryOwner,
|
IsAdmin: m.IsAdmin || m.IsOwner || m.IsPrimaryOwner,
|
||||||
ExternalID: m.ID,
|
ExternalID: m.ID,
|
||||||
MFAStatus: slackMFAStatus(m.Has2FA),
|
MFAStatus: slackMFAStatus(m.Has2FA),
|
||||||
|
|||||||
@@ -65,7 +65,6 @@ func (d *SupabaseDriver) ListAccounts(ctx context.Context) ([]AccountRecord, err
|
|||||||
Email: m.Email,
|
Email: m.Email,
|
||||||
FullName: m.UserName,
|
FullName: m.UserName,
|
||||||
Role: m.RoleName,
|
Role: m.RoleName,
|
||||||
Active: true,
|
|
||||||
IsAdmin: isAdmin,
|
IsAdmin: isAdmin,
|
||||||
ExternalID: m.UserID,
|
ExternalID: m.UserID,
|
||||||
MFAStatus: mfaStatus,
|
MFAStatus: mfaStatus,
|
||||||
|
|||||||
@@ -115,7 +115,7 @@ func (d *TallyDriver) listUsers(ctx context.Context) ([]AccountRecord, error) {
|
|||||||
record := AccountRecord{
|
record := AccountRecord{
|
||||||
Email: u.Email,
|
Email: u.Email,
|
||||||
FullName: u.FullName,
|
FullName: u.FullName,
|
||||||
Active: !u.IsDeleted,
|
Active: new(!u.IsDeleted),
|
||||||
ExternalID: u.ID,
|
ExternalID: u.ID,
|
||||||
MFAStatus: mfaStatus,
|
MFAStatus: mfaStatus,
|
||||||
AuthMethod: coredata.AccessEntryAuthMethodUnknown,
|
AuthMethod: coredata.AccessEntryAuthMethodUnknown,
|
||||||
@@ -169,7 +169,7 @@ func (d *TallyDriver) listInvites(ctx context.Context) ([]AccountRecord, error)
|
|||||||
for _, inv := range invites {
|
for _, inv := range invites {
|
||||||
record := AccountRecord{
|
record := AccountRecord{
|
||||||
Email: inv.Email,
|
Email: inv.Email,
|
||||||
Active: false,
|
Active: new(false),
|
||||||
ExternalID: inv.ID,
|
ExternalID: inv.ID,
|
||||||
MFAStatus: coredata.MFAStatusUnknown,
|
MFAStatus: coredata.MFAStatusUnknown,
|
||||||
AuthMethod: coredata.AccessEntryAuthMethodUnknown,
|
AuthMethod: coredata.AccessEntryAuthMethodUnknown,
|
||||||
|
|||||||
@@ -14,7 +14,9 @@
|
|||||||
|
|
||||||
package accessreview
|
package accessreview
|
||||||
|
|
||||||
import "testing"
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
func TestNormalizeAccountKey(t *testing.T) {
|
func TestNormalizeAccountKey(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|||||||
Reference in New Issue
Block a user