Skip to content

Commit c2d579f

Browse files
committed
guid resolver tweaks
1 parent 619e9cd commit c2d579f

File tree

2 files changed

+400
-49
lines changed

2 files changed

+400
-49
lines changed

src/hooks/use-guid-resolver.js

Lines changed: 158 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,52 @@ const extractGuidsFromString = (str) => {
1616
return str.match(guidRegex) || [];
1717
};
1818

19+
// Function to extract object IDs from partner tenant UPNs (user_<objectid>@<tenant>.onmicrosoft.com)
20+
// Also handles format: TenantName.onmicrosoft.com\tenant: <tenant-guid>, object: <object-guid>
21+
const extractObjectIdFromPartnerUPN = (str) => {
22+
if (typeof str !== "string") return [];
23+
const matches = [];
24+
25+
// Format 1: user_<objectid>@<tenant>.onmicrosoft.com
26+
const partnerUpnRegex = /user_([0-9a-f]{32})@([^@]+\.onmicrosoft\.com)/gi;
27+
let match;
28+
29+
while ((match = partnerUpnRegex.exec(str)) !== null) {
30+
// Convert the 32-character hex string to GUID format
31+
const hexId = match[1];
32+
const tenantDomain = match[2];
33+
if (hexId.length === 32) {
34+
const guid = [
35+
hexId.slice(0, 8),
36+
hexId.slice(8, 12),
37+
hexId.slice(12, 16),
38+
hexId.slice(16, 20),
39+
hexId.slice(20, 32),
40+
].join("-");
41+
matches.push({ guid, tenantDomain });
42+
}
43+
}
44+
45+
// Format 2: TenantName.onmicrosoft.com\tenant: <tenant-guid>, object: <object-guid>
46+
// For exchange format, use the partner tenant guid for resolution
47+
const partnerTenantObjectRegex =
48+
/([^\\]+\.onmicrosoft\.com)\\tenant:\s*([0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}),\s*object:\s*([0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12})/gi;
49+
50+
while ((match = partnerTenantObjectRegex.exec(str)) !== null) {
51+
const customerTenantDomain = match[1]; // This is the customer tenant domain
52+
const partnerTenantGuid = match[2]; // This is the partner tenant guid - use this for resolution
53+
const objectGuid = match[3]; // This is the object to resolve
54+
55+
// Use the partner tenant GUID for resolution
56+
matches.push({ guid: objectGuid, tenantDomain: partnerTenantGuid });
57+
}
58+
59+
return matches;
60+
};
61+
1962
// Function to recursively scan an object for GUIDs
20-
const findGuids = (obj, guidsSet = new Set()) => {
21-
if (!obj) return guidsSet;
63+
const findGuids = (obj, guidsSet = new Set(), partnerGuidsMap = new Map()) => {
64+
if (!obj) return { guidsSet, partnerGuidsMap };
2265

2366
if (typeof obj === "string") {
2467
// Check if the entire string is a GUID
@@ -28,14 +71,31 @@ const findGuids = (obj, guidsSet = new Set()) => {
2871
// Extract GUIDs embedded within longer strings
2972
const embeddedGuids = extractGuidsFromString(obj);
3073
embeddedGuids.forEach((guid) => guidsSet.add(guid));
74+
75+
// Extract object IDs from partner tenant UPNs
76+
const partnerObjectIds = extractObjectIdFromPartnerUPN(obj);
77+
partnerObjectIds.forEach(({ guid, tenantDomain }) => {
78+
if (!partnerGuidsMap.has(tenantDomain)) {
79+
partnerGuidsMap.set(tenantDomain, new Set());
80+
}
81+
partnerGuidsMap.get(tenantDomain).add(guid);
82+
});
3183
}
3284
} else if (Array.isArray(obj)) {
33-
obj.forEach((item) => findGuids(item, guidsSet));
85+
obj.forEach((item) => {
86+
const result = findGuids(item, guidsSet, partnerGuidsMap);
87+
guidsSet = result.guidsSet;
88+
partnerGuidsMap = result.partnerGuidsMap;
89+
});
3490
} else if (typeof obj === "object") {
35-
Object.values(obj).forEach((value) => findGuids(value, guidsSet));
91+
Object.values(obj).forEach((value) => {
92+
const result = findGuids(value, guidsSet, partnerGuidsMap);
93+
guidsSet = result.guidsSet;
94+
partnerGuidsMap = result.partnerGuidsMap;
95+
});
3696
}
3797

38-
return guidsSet;
98+
return { guidsSet, partnerGuidsMap };
3999
};
40100

41101
export const useGuidResolver = () => {
@@ -48,6 +108,7 @@ export const useGuidResolver = () => {
48108
// Use refs for values that shouldn't trigger re-renders but need to persist
49109
const notFoundGuidsRef = useRef(new Set());
50110
const pendingGuidsRef = useRef([]);
111+
const pendingPartnerGuidsRef = useRef(new Map()); // Map of tenantDomain -> Set of GUIDs
51112
const lastRequestTimeRef = useRef(0);
52113

53114
// Setup API call for directory objects resolution
@@ -81,46 +142,110 @@ export const useGuidResolver = () => {
81142
},
82143
});
83144

145+
// Setup API call for partner tenant directory objects resolution
146+
const partnerDirectoryObjectsMutation = ApiPostCall({
147+
relatedQueryKeys: ["partnerDirectoryObjects"],
148+
onResult: (data) => {
149+
if (data && Array.isArray(data.value)) {
150+
const newMapping = {};
151+
152+
// Process the returned results
153+
data.value.forEach((item) => {
154+
if (item.id && (item.displayName || item.userPrincipalName || item.mail)) {
155+
newMapping[item.id] = item.displayName || item.userPrincipalName || item.mail;
156+
}
157+
});
158+
159+
setGuidMapping((prevMapping) => ({ ...prevMapping, ...newMapping }));
160+
161+
// Clear processed partner GUIDs
162+
pendingPartnerGuidsRef.current = new Map();
163+
setIsLoadingGuids(false);
164+
}
165+
},
166+
});
167+
84168
// Function to handle resolving GUIDs
85169
const resolveGuids = useCallback(
86170
(objectToScan) => {
87-
const guidsSet = findGuids(objectToScan);
171+
const { guidsSet, partnerGuidsMap } = findGuids(objectToScan);
88172

89-
if (guidsSet.size === 0) return;
173+
// Handle regular GUIDs (current tenant)
174+
if (guidsSet.size > 0) {
175+
const guidsArray = Array.from(guidsSet);
176+
const notResolvedGuids = guidsArray.filter(
177+
(guid) => !guidMapping[guid] && !notFoundGuidsRef.current.has(guid)
178+
);
90179

91-
const guidsArray = Array.from(guidsSet);
92-
const notResolvedGuids = guidsArray.filter(
93-
(guid) => !guidMapping[guid] && !notFoundGuidsRef.current.has(guid)
94-
);
180+
if (notResolvedGuids.length > 0) {
181+
const allPendingGuids = [...new Set([...pendingGuidsRef.current, ...notResolvedGuids])];
182+
pendingGuidsRef.current = allPendingGuids;
183+
setIsLoadingGuids(true);
95184

96-
if (notResolvedGuids.length === 0) return;
185+
// Implement throttling - only send a new request every 2 seconds
186+
const now = Date.now();
187+
if (now - lastRequestTimeRef.current >= 2000) {
188+
lastRequestTimeRef.current = now;
97189

98-
const allPendingGuids = [...new Set([...pendingGuidsRef.current, ...notResolvedGuids])];
99-
pendingGuidsRef.current = allPendingGuids;
100-
setIsLoadingGuids(true);
190+
// Only send a maximum of 1000 GUIDs per request
191+
const batchSize = 1000;
192+
const guidsToSend = allPendingGuids.slice(0, batchSize);
101193

102-
// Implement throttling - only send a new request every 2 seconds
103-
const now = Date.now();
104-
if (now - lastRequestTimeRef.current < 2000) {
105-
return;
194+
if (guidsToSend.length > 0) {
195+
directoryObjectsMutation.mutate({
196+
url: "/api/ListDirectoryObjects",
197+
data: {
198+
tenantFilter: tenantFilter,
199+
ids: guidsToSend,
200+
$select: "id,displayName,userPrincipalName,mail",
201+
},
202+
});
203+
} else {
204+
setIsLoadingGuids(false);
205+
}
206+
}
207+
}
106208
}
107209

108-
lastRequestTimeRef.current = now;
210+
// Handle partner tenant GUIDs
211+
if (partnerGuidsMap.size > 0) {
212+
partnerGuidsMap.forEach((guids, tenantDomain) => {
213+
const guidsArray = Array.from(guids);
214+
const notResolvedGuids = guidsArray.filter(
215+
(guid) => !guidMapping[guid] && !notFoundGuidsRef.current.has(guid)
216+
);
217+
218+
if (notResolvedGuids.length > 0) {
219+
// Store pending partner GUIDs
220+
if (!pendingPartnerGuidsRef.current.has(tenantDomain)) {
221+
pendingPartnerGuidsRef.current.set(tenantDomain, new Set());
222+
}
223+
notResolvedGuids.forEach((guid) =>
224+
pendingPartnerGuidsRef.current.get(tenantDomain).add(guid)
225+
);
109226

110-
// Only send a maximum of 1000 GUIDs per request
111-
const batchSize = 1000;
112-
const guidsToSend = allPendingGuids.slice(0, batchSize);
227+
setIsLoadingGuids(true);
113228

114-
if (guidsToSend.length > 0) {
115-
directoryObjectsMutation.mutate({
116-
url: "/api/ListDirectoryObjects",
117-
data: {
118-
tenantFilter: tenantFilter,
119-
ids: guidsToSend,
120-
$select: "id,displayName,userPrincipalName,mail",
121-
},
229+
// Make API call for partner tenant
230+
const batchSize = 1000;
231+
const guidsToSend = notResolvedGuids.slice(0, batchSize);
232+
233+
if (guidsToSend.length > 0) {
234+
partnerDirectoryObjectsMutation.mutate({
235+
url: "/api/ListDirectoryObjects",
236+
data: {
237+
tenantFilter: tenantDomain,
238+
ids: guidsToSend,
239+
$select: "id,displayName,userPrincipalName,mail",
240+
},
241+
});
242+
}
243+
}
122244
});
123-
} else {
245+
}
246+
247+
// If no GUIDs to process, ensure loading state is false
248+
if (guidsSet.size === 0 && partnerGuidsMap.size === 0) {
124249
setIsLoadingGuids(false);
125250
}
126251
},
@@ -132,5 +257,6 @@ export const useGuidResolver = () => {
132257
isLoadingGuids,
133258
resolveGuids,
134259
isGuid,
260+
extractObjectIdFromPartnerUPN,
135261
};
136262
};

0 commit comments

Comments
 (0)