Skip to content

Commit 7dba309

Browse files
committed
Warn when principal does not have AuditLog.Read.All for User.signInActivity request
1 parent 97e50a7 commit 7dba309

File tree

2 files changed

+59
-21
lines changed

2 files changed

+59
-21
lines changed

cmd/list-users.go

Lines changed: 52 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"context"
2222
"os"
2323
"os/signal"
24+
"strings"
2425
"time"
2526

2627
"github.com/bloodhoundad/azurehound/v2/client"
@@ -61,30 +62,36 @@ func listUsersCmdImpl(cmd *cobra.Command, _ []string) {
6162
func listUsers(ctx context.Context, client client.AzureClient) <-chan interface{} {
6263
out := make(chan interface{})
6364

64-
params := query.GraphParams{Select: []string{
65-
"accountEnabled",
66-
"createdDateTime",
67-
"displayName",
68-
"jobTitle",
69-
"lastPasswordChangeDateTime",
70-
"mail",
71-
"onPremisesSecurityIdentifier",
72-
"onPremisesSyncEnabled",
73-
"signInActivity",
74-
"userPrincipalName",
75-
"userType",
76-
"id",
77-
}}
65+
makeParams := func(includeSignInActivity bool) query.GraphParams {
66+
selectCols := []string{
67+
"accountEnabled",
68+
"createdDateTime",
69+
"displayName",
70+
"jobTitle",
71+
"lastPasswordChangeDateTime",
72+
"mail",
73+
"onPremisesSecurityIdentifier",
74+
"onPremisesSyncEnabled",
75+
"userPrincipalName",
76+
"userType",
77+
"id",
78+
}
79+
if includeSignInActivity {
80+
selectCols = append(selectCols, "signInActivity")
81+
}
82+
return query.GraphParams{Select: selectCols}
83+
}
7884

7985
go func() {
8086
defer panicrecovery.PanicRecovery()
8187
defer close(out)
82-
count := 0
83-
for item := range client.ListAzureADUsers(ctx, params) {
84-
if item.Error != nil {
85-
log.Error(item.Error, "unable to continue processing users")
86-
return
87-
} else {
88+
89+
streamOnce := func(params query.GraphParams) (int, error) {
90+
count := 0
91+
for item := range client.ListAzureADUsers(ctx, params) {
92+
if item.Error != nil {
93+
return count, item.Error
94+
}
8895
log.V(2).Info("found user", "id", item.Ok.Id)
8996
count++
9097
user := models.User{
@@ -96,12 +103,36 @@ func listUsers(ctx context.Context, client client.AzureClient) <-chan interface{
96103
Kind: enums.KindAZUser,
97104
Data: user,
98105
}); !ok {
99-
return
106+
return count, nil
100107
}
101108
}
109+
return count, nil
110+
}
111+
112+
includeSignInActivity := true
113+
params := makeParams(true)
114+
count, err := streamOnce(params)
115+
if err != nil && includeSignInActivity && count == 0 && isGraphAuthorizationDenied(err) {
116+
log.Info("warning: authorization denied when requesting signInActivity for users (missing AuditLog.Read.All API permission); retrying without signInActivity")
117+
includeSignInActivity = false
118+
params = makeParams(false)
119+
count, err = streamOnce(params)
120+
}
121+
if err != nil {
122+
log.Error(err, "unable to continue processing users")
123+
return
102124
}
103125
log.Info("finished listing all users", "count", count)
104126
}()
105127

106128
return out
107129
}
130+
131+
func isGraphAuthorizationDenied(err error) bool {
132+
if err == nil {
133+
return false
134+
}
135+
msg := err.Error()
136+
msgLower := strings.ToLower(msg)
137+
return strings.Contains(msg, "Authentication_MSGraphPermissionMissing") && strings.Contains(msgLower, "auditlog.read.all")
138+
}

cmd/list-users_test.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,3 +68,10 @@ func TestListUsers(t *testing.T) {
6868
t.Error("expected channel to close from an error result but it did not")
6969
}
7070
}
71+
72+
func TestIsGraphAuthorizationDenied(t *testing.T) {
73+
err := fmt.Errorf("map[error:map[code:Authentication_MSGraphPermissionMissing innerError:map[client-request-id:fac52490-ea06-48f1-941e-5f5bba8e35fc date:2026-01-28T15:29:16 request-id:fac52490-ea06-48f1-941e-5f5bba8e35fc] message:The principal does not have required Microsoft Graph permission(s): AuditLog.Read.All to call this API. For more information about Microsoft Graph permissions, please visit https://learn.microsoft.com/graph/permissions-overview.]]")
74+
if !isGraphAuthorizationDenied(err) {
75+
t.Errorf("expected true")
76+
}
77+
}

0 commit comments

Comments
 (0)