@@ -38,6 +38,26 @@ def authenticate(self):
38
38
39
39
return token_r .json ().get ('access_token' )
40
40
41
+ def resolve_user_guid (self , email , headers , base_url ):
42
+ """Resolves a userPrincipalName (email) to an objectId (GUID) using direct lookup (most compatible)."""
43
+ url = f"{ base_url } users/{ email } ?$select=id"
44
+ response = requests .get (url , headers = headers )
45
+
46
+ if response .status_code != 200 :
47
+ self .error (f"Failed to resolve GUID for user { email } : { response .content } " )
48
+
49
+ user = response .json ()
50
+ user_id = user .get ("id" )
51
+ if not user_id :
52
+ self .error (f"ID not found in response for { email } " )
53
+
54
+ return user_id
55
+
56
+ def ensure_user_guid (self , base_url , headers ):
57
+ if "@" in self .user :
58
+ return self .resolve_user_guid (self .user , headers , base_url )
59
+ return self .user
60
+
41
61
def handle_get_signins (self , headers , base_url ):
42
62
"""
43
63
Retrieve sign-in logs for a userPrincipalName within a specified time range.
@@ -49,14 +69,15 @@ def handle_get_signins(self, headers, base_url):
49
69
self .user = self .get_data ()
50
70
if not self .user :
51
71
self .error ("No user supplied" )
52
-
72
+ self .guid = self .ensure_user_guid (base_url , headers )
73
+
53
74
# Build the filter time
54
75
filter_time = datetime .utcnow () - timedelta (days = self .time_range )
55
76
format_time = filter_time .strftime ('%Y-%m-%dT00:00:00Z' )
56
77
57
78
# Query sign-in logs
58
79
endpoint = (
59
- f"auditLogs/signIns?$filter=startsWith(userPrincipalName, '{ self .user } ') "
80
+ f"auditLogs/signIns?$filter=userId eq '{ self .guid } ' "
60
81
f"and createdDateTime ge { format_time } &$top={ self .lookup_limit } "
61
82
)
62
83
r = requests .get (base_url + endpoint , headers = headers )
@@ -168,13 +189,14 @@ def handle_get_userinfo(self, headers, base_url):
168
189
self .user = self .get_data ()
169
190
if not self .user :
170
191
self .error ("No user supplied" )
171
-
192
+
193
+ self .guid = self .ensure_user_guid (base_url , headers )
172
194
# Use select to retrieve many user attributes. Adjust as needed.
173
195
params = {
174
196
"$select" : "," .join (self .params_list )
175
197
}
176
198
177
- user_info_url = f"{ base_url } users/{ self .user } "
199
+ user_info_url = f"{ base_url } users/{ self .guid } "
178
200
# user_info_url = f"{base_url}users/{self.user}"
179
201
180
202
user_response = requests .get (user_info_url , headers = headers , params = params )
@@ -211,7 +233,7 @@ def handle_get_userinfo(self, headers, base_url):
211
233
}
212
234
213
235
# Fetch user's manager
214
- manager_url = f"{ base_url } users/{ self .user } /manager?$select=id,displayName,userPrincipalName"
236
+ manager_url = f"{ base_url } users/{ self .guid } /manager?$select=id,displayName,userPrincipalName"
215
237
manager_resp = requests .get (manager_url , headers = headers )
216
238
if manager_resp .status_code == 200 :
217
239
manager_data = manager_resp .json ()
@@ -224,7 +246,7 @@ def handle_get_userinfo(self, headers, base_url):
224
246
}
225
247
226
248
# Fetch user's license details
227
- license_url = f"{ base_url } users/{ self .user } /licenseDetails"
249
+ license_url = f"{ base_url } users/{ self .guid } /licenseDetails"
228
250
license_resp = requests .get (license_url , headers = headers )
229
251
if license_resp .status_code == 200 :
230
252
license_data = license_resp .json ().get ("value" , [])
@@ -238,7 +260,7 @@ def handle_get_userinfo(self, headers, base_url):
238
260
})
239
261
240
262
# Fetch user's group memberships
241
- member_of_url = f"{ base_url } users/{ self .user } /memberOf"
263
+ member_of_url = f"{ base_url } users/{ self .guid } /memberOf"
242
264
member_of_response = requests .get (member_of_url , headers = headers )
243
265
if member_of_response .status_code == 200 :
244
266
memberships = member_of_response .json ().get ("value" , [])
@@ -249,7 +271,7 @@ def handle_get_userinfo(self, headers, base_url):
249
271
})
250
272
251
273
# MFA Methods
252
- mfa_url = f"{ base_url } users/{ self .user } /authentication/methods"
274
+ mfa_url = f"{ base_url } users/{ self .guid } /authentication/methods"
253
275
mfa_r = requests .get (mfa_url , headers = headers )
254
276
255
277
if mfa_r .status_code == 200 :
@@ -374,9 +396,11 @@ def handle_get_directoryAuditLogs(self, headers, base_url):
374
396
self .error ('Incorrect dataType. "mail" expected.' )
375
397
try :
376
398
# Pull the userPrincipalName from the observable data (data_type=mail)
377
- user_upn = self .get_data ()
378
- if not user_upn :
399
+ self . user = self .get_data ()
400
+ if not self . user :
379
401
self .error ("No user principal name supplied for directory audit logs" )
402
+ self .guid = self .ensure_user_guid (base_url , headers )
403
+
380
404
# Calculate time range (past X days)
381
405
filter_time = datetime .utcnow () - timedelta (days = self .time_range )
382
406
filter_time_str = filter_time .strftime ('%Y-%m-%dT%H:%M:%SZ' )
@@ -386,7 +410,7 @@ def handle_get_directoryAuditLogs(self, headers, base_url):
386
410
endpoint = (
387
411
"auditLogs/directoryAudits?"
388
412
f"$filter=activityDateTime ge { filter_time_str } "
389
- f"and initiatedBy/user/userPrincipalName eq '{ user_upn } '"
413
+ f"and initiatedBy/user/id eq '{ self . guid } '"
390
414
f"&$top={ self .lookup_limit } "
391
415
)
392
416
@@ -435,16 +459,19 @@ def handle_get_devices(self, headers, base_url):
435
459
else :
436
460
self .error ("No user UPN supplied" )
437
461
438
- # Build the appropriate endpoint based on the observable type
439
- if self .data_type == 'hostname' :
462
+ if self .data_type == 'mail' :
463
+ # Resolve UPN to GUID and use exact match
464
+ self .user = query_value
465
+ guid = self .ensure_user_guid (base_url , headers )
440
466
endpoint = (
441
- "deviceManagement/managedDevices?"
442
- f"$filter=startswith(deviceName,' { query_value } ') "
467
+ f "deviceManagement/managedDevices?"
468
+ f"$filter=userId eq ' { guid } ' "
443
469
)
444
- elif self .data_type == 'mail' :
470
+ else :
471
+ # Use startswith for partial hostname matches
445
472
endpoint = (
446
- "deviceManagement/managedDevices?"
447
- f"$filter=startswith(userPrincipalName ,'{ query_value } ')"
473
+ f "deviceManagement/managedDevices?"
474
+ f"$filter=startswith(deviceName ,'{ query_value } ')"
448
475
)
449
476
450
477
# Perform the GET request
0 commit comments