From 02b4ce54d37dd9b0372d893bdefd36d4a53558ee Mon Sep 17 00:00:00 2001 From: John Duprey Date: Tue, 20 Jan 2026 11:04:54 -0500 Subject: [PATCH 1/5] Pass dateFilter to log entry view and API call Updated the log entry view action to include the dateFilter parameter in the URL. Modified logentry.js to extract dateFilter from the query and pass it to the API call for fetching log details. Also improved code formatting for severity badge rendering. --- src/pages/cipp/logs/index.js | 12 ++++++------ src/pages/cipp/logs/logentry.js | 15 ++++++++------- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/pages/cipp/logs/index.js b/src/pages/cipp/logs/index.js index 18f8ad68174c..41eac2225d46 100644 --- a/src/pages/cipp/logs/index.js +++ b/src/pages/cipp/logs/index.js @@ -37,7 +37,7 @@ const pageTitle = "Logbook Results"; const actions = [ { label: "View Log Entry", - link: "/cipp/logs/logentry?logentry=[RowKey]", + link: "/cipp/logs/logentry?logentry=[RowKey]&dateFilter=[DateFilter]", icon: , color: "primary", }, @@ -100,14 +100,14 @@ const Page = () => { setStartDate( data.startDate ? new Date(data.startDate * 1000).toISOString().split("T")[0].replace(/-/g, "") - : null + : null, ); // Format end date if available setEndDate( data.endDate ? new Date(data.endDate * 1000).toISOString().split("T")[0].replace(/-/g, "") - : null + : null, ); // Set username filter if available @@ -117,7 +117,7 @@ const Page = () => { setSeverity( data.severity && data.severity.length > 0 ? data.severity.map((item) => item.value).join(",") - : null + : null, ); // Close the accordion after applying filters @@ -157,13 +157,13 @@ const Page = () => { <> {startDate ? new Date( - startDate.replace(/(\d{4})(\d{2})(\d{2})/, "$1-$2-$3") + "T00:00:00" + startDate.replace(/(\d{4})(\d{2})(\d{2})/, "$1-$2-$3") + "T00:00:00", ).toLocaleDateString() : new Date().toLocaleDateString()} {startDate && endDate ? " - " : ""} {endDate ? new Date( - endDate.replace(/(\d{4})(\d{2})(\d{2})/, "$1-$2-$3") + "T00:00:00" + endDate.replace(/(\d{4})(\d{2})(\d{2})/, "$1-$2-$3") + "T00:00:00", ).toLocaleDateString() : ""} diff --git a/src/pages/cipp/logs/logentry.js b/src/pages/cipp/logs/logentry.js index 2dbb4a23a5a7..175dcc915aba 100644 --- a/src/pages/cipp/logs/logentry.js +++ b/src/pages/cipp/logs/logentry.js @@ -11,12 +11,13 @@ import { getCippTranslation } from "../../../utils/get-cipp-translation"; const Page = () => { const router = useRouter(); - const { logentry } = router.query; + const { logentry, dateFilter } = router.query; const logRequest = ApiGetCall({ url: `/api/Listlogs`, data: { logentryid: logentry, + dateFilter: dateFilter, }, queryKey: `GetLogEntry-${logentry}`, waiting: !!logentry, @@ -44,12 +45,12 @@ const Page = () => { logData.Severity === "CRITICAL" ? "error" : logData.Severity === "Error" - ? "error" - : logData.Severity === "Warn" - ? "warning" - : logData.Severity === "Info" - ? "info" - : "default" + ? "error" + : logData.Severity === "Warn" + ? "warning" + : logData.Severity === "Info" + ? "info" + : "default" } variant="filled" /> From c17dceae613ef8fb329b85ddf4aefaff248d65e3 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Tue, 20 Jan 2026 12:01:39 -0500 Subject: [PATCH 2/5] Improve error handling and retry logic in API calls Added status code 302 and 500 to the list of HTTP statuses that should not trigger retries in ApiGetCallWithPagination. When a 302 redirect to the AAD login is detected, the 'authmecipp' query is invalidated. Also fixed minor formatting and trailing comma issues in several functions. --- src/api/ApiCall.jsx | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/api/ApiCall.jsx b/src/api/ApiCall.jsx index e4d9a366908c..a2d7d0396ce1 100644 --- a/src/api/ApiCall.jsx +++ b/src/api/ApiCall.jsx @@ -50,7 +50,7 @@ export function ApiGetCall(props) { title: `${ error.config?.params?.tenantFilter ? error.config?.params?.tenantFilter : "" } Error`, - }) + }), ); } return returnRetry; @@ -211,7 +211,7 @@ export function ApiPostCall({ relatedQueryKeys, onResult }) { if (!query.queryKey || !query.queryKey[0]) return false; const queryKeyStr = String(query.queryKey[0]); const matches = wildcardPatterns.some((pattern) => - queryKeyStr.startsWith(pattern) + queryKeyStr.startsWith(pattern), ); // Debug logging for each query check @@ -220,7 +220,7 @@ export function ApiPostCall({ relatedQueryKeys, onResult }) { queryKey: query.queryKey, queryKeyStr, matchedPattern: wildcardPatterns.find((pattern) => - queryKeyStr.startsWith(pattern) + queryKeyStr.startsWith(pattern), ), }); } @@ -252,8 +252,9 @@ export function ApiGetCallWithPagination({ waiting = true, }) { const dispatch = useDispatch(); + const queryClient = useQueryClient(); const MAX_RETRIES = retry; - const HTTP_STATUS_TO_NOT_RETRY = [401, 403, 404]; + const HTTP_STATUS_TO_NOT_RETRY = [302, 401, 403, 404, 500]; const retryFn = (failureCount, error) => { let returnRetry = true; @@ -261,6 +262,12 @@ export function ApiGetCallWithPagination({ returnRetry = false; } if (isAxiosError(error) && HTTP_STATUS_TO_NOT_RETRY.includes(error.response?.status ?? 0)) { + if ( + error.response?.status === 302 && + error.response?.headers.get("location").includes("/.auth/login/aad") + ) { + queryClient.invalidateQueries({ queryKey: ["authmecipp"] }); + } returnRetry = false; } @@ -270,7 +277,7 @@ export function ApiGetCallWithPagination({ message: getCippError(error), title: "Error", toastError: error, - }) + }), ); } return returnRetry; From 8e0c76f20fdf38fc408f6114be85daee751ae10a Mon Sep 17 00:00:00 2001 From: John Duprey Date: Tue, 20 Jan 2026 12:17:58 -0500 Subject: [PATCH 3/5] Add calendar permissions report page Introduces a new page for viewing calendar permissions, allowing grouping by user or calendar. Includes cache sync functionality and displays cached data from the reporting database, with support for multi-tenant views. --- src/layouts/config.js | 5 + .../reports/calendar-permissions/index.js | 117 ++++++++++++++++++ 2 files changed, 122 insertions(+) create mode 100644 src/pages/email/reports/calendar-permissions/index.js diff --git a/src/layouts/config.js b/src/layouts/config.js index 8e8fd1f97c39..82e68a157e9c 100644 --- a/src/layouts/config.js +++ b/src/layouts/config.js @@ -721,6 +721,11 @@ export const nativeMenuItems = [ path: "/email/reports/mailbox-permissions", permissions: ["Exchange.Mailbox.*"], }, + { + title: "Calendar Permissions", + path: "/email/reports/calendar-permissions", + permissions: ["Exchange.Mailbox.*"], + }, { title: "Anti-Phishing Filters", path: "/email/reports/antiphishing-filters", diff --git a/src/pages/email/reports/calendar-permissions/index.js b/src/pages/email/reports/calendar-permissions/index.js new file mode 100644 index 000000000000..d945e29b8c10 --- /dev/null +++ b/src/pages/email/reports/calendar-permissions/index.js @@ -0,0 +1,117 @@ +import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; +import { useState } from "react"; +import { + Button, + FormControlLabel, + Switch, + Alert, + SvgIcon, + IconButton, + Tooltip, +} from "@mui/material"; +import { useSettings } from "../../../../hooks/use-settings"; +import { Stack } from "@mui/system"; +import { Sync, Info } from "@mui/icons-material"; +import { useDialog } from "../../../../hooks/use-dialog"; +import { CippApiDialog } from "../../../../components/CippComponents/CippApiDialog"; + +const Page = () => { + const [byUser, setByUser] = useState(true); + const currentTenant = useSettings().currentTenant; + const syncDialog = useDialog(); + + const isAllTenants = currentTenant === "AllTenants"; + + const columns = byUser + ? [ + ...(isAllTenants ? ["Tenant"] : []), + "User", + "UserMailboxType", + "Permissions", + "MailboxCacheTimestamp", + "PermissionCacheTimestamp", + ] + : [ + ...(isAllTenants ? ["Tenant"] : []), + "CalendarUPN", + "CalendarDisplayName", + "CalendarType", + "Permissions", + "MailboxCacheTimestamp", + "PermissionCacheTimestamp", + ]; + + // Compute apiData based on byUser directly (no useState needed) + const apiData = { + UseReportDB: true, + ByUser: byUser, + }; + + const pageActions = [ + + + + + + + + setByUser(e.target.checked)} color="primary" /> + } + label="Group by User" + labelPlacement="start" + /> + , + ]; + + return ( + <> + {currentTenant && currentTenant !== "" ? ( + + ) : ( + Please select a tenant to view calendar permissions. + )} + + + ); +}; + +Page.getLayout = (page) => {page}; + +export default Page; From bcd57dc2b88c236a58b39d35620b218b3928ff0d Mon Sep 17 00:00:00 2001 From: KelvinTegelaar <49186168+KelvinTegelaar@users.noreply.github.com> Date: Tue, 20 Jan 2026 18:40:04 +0100 Subject: [PATCH 4/5] Fixes drift issues --- src/pages/tenant/manage/applied-standards.js | 1007 +++++++++++++----- 1 file changed, 764 insertions(+), 243 deletions(-) diff --git a/src/pages/tenant/manage/applied-standards.js b/src/pages/tenant/manage/applied-standards.js index 5b6539767fce..ef11f5e33e34 100644 --- a/src/pages/tenant/manage/applied-standards.js +++ b/src/pages/tenant/manage/applied-standards.js @@ -35,6 +35,7 @@ import { NotificationImportant, Construction, Schedule, + Check, } from "@mui/icons-material"; import standards from "/src/data/standards.json"; import { CippApiDialog } from "../../../components/CippComponents/CippApiDialog"; @@ -91,7 +92,7 @@ const Page = () => { // Selected template object (safe lookup) const selectedTemplate = useMemo( () => templates.find((t) => t.GUID === templateId), - [templates, templateId] + [templates, templateId], ); // Run the report once @@ -123,7 +124,7 @@ const Page = () => { useEffect(() => { if (templateId && templateDetails.isSuccess && templateDetails.data) { const selectedTemplate = templateDetails.data.find( - (template) => template.GUID === templateId + (template) => template.GUID === templateId, ); if (selectedTemplate && comparisonApi.isSuccess && comparisonApi.data) { @@ -157,30 +158,55 @@ const Page = () => { const itemTemplateId = expandedTemplate.GUID; const standardId = `standards.IntuneTemplate.${itemTemplateId}`; const standardInfo = standards.find( - (s) => s.name === `standards.IntuneTemplate` + (s) => s.name === `standards.IntuneTemplate`, ); // Find the tenant's value for this specific template const currentTenantStandard = currentTenantData.find( - (s) => s.standardId === standardId + (s) => s.standardId === standardId, ); // Get the standard object and its value from the tenant object const standardObject = currentTenantObj?.[standardId]; const directStandardValue = standardObject?.Value; - // Determine compliance status + // Determine compliance status - match main logic let isCompliant = false; - // For IntuneTemplate, the value is true if compliant, or an object with comparison data if not compliant - if (directStandardValue === true) { - isCompliant = true; - } else if ( - directStandardValue !== undefined && - typeof directStandardValue !== "object" + // FIRST: Check if CurrentValue and ExpectedValue exist and match + if ( + standardObject?.CurrentValue !== undefined && + standardObject?.ExpectedValue !== undefined ) { + const sortedCurrent = + typeof standardObject.CurrentValue === "object" && + standardObject.CurrentValue !== null + ? Object.keys(standardObject.CurrentValue) + .sort() + .reduce((obj, key) => { + obj[key] = standardObject.CurrentValue[key]; + return obj; + }, {}) + : standardObject.CurrentValue; + const sortedExpected = + typeof standardObject.ExpectedValue === "object" && + standardObject.ExpectedValue !== null + ? Object.keys(standardObject.ExpectedValue) + .sort() + .reduce((obj, key) => { + obj[key] = standardObject.ExpectedValue[key]; + return obj; + }, {}) + : standardObject.ExpectedValue; + isCompliant = + JSON.stringify(sortedCurrent) === JSON.stringify(sortedExpected); + } + // SECOND: Check if Value is explicitly true + else if (directStandardValue === true) { isCompliant = true; - } else if (currentTenantStandard) { + } + // THIRD: Fall back to currentTenantStandard + else if (currentTenantStandard) { isCompliant = currentTenantStandard.value === true; } @@ -220,8 +246,8 @@ const Page = () => { complianceStatus: isOverridden ? "Overridden" : isCompliant - ? "Compliant" - : "Non-Compliant", + ? "Compliant" + : "Non-Compliant", isOverridden, overridingTemplateId: isOverridden ? tenantTemplateId : null, overridingTemplateName, @@ -258,12 +284,12 @@ const Page = () => { if (itemTemplateId) { const standardId = `standards.IntuneTemplate.${itemTemplateId}`; const standardInfo = standards.find( - (s) => s.name === `standards.IntuneTemplate` + (s) => s.name === `standards.IntuneTemplate`, ); // Find the tenant's value for this specific template const currentTenantStandard = currentTenantData.find( - (s) => s.standardId === standardId + (s) => s.standardId === standardId, ); // Get the standard object and its value from the tenant object @@ -320,8 +346,8 @@ const Page = () => { complianceStatus: isOverridden ? "Overridden" : isCompliant - ? "Compliant" - : "Non-Compliant", + ? "Compliant" + : "Non-Compliant", isOverridden, overridingTemplateId: isOverridden ? tenantTemplateId : null, overridingTemplateName, @@ -374,12 +400,12 @@ const Page = () => { const itemTemplateId = expandedTemplate.GUID; const standardId = `standards.ConditionalAccessTemplate.${itemTemplateId}`; const standardInfo = standards.find( - (s) => s.name === `standards.ConditionalAccessTemplate` + (s) => s.name === `standards.ConditionalAccessTemplate`, ); // Find the tenant's value for this specific template const currentTenantStandard = currentTenantData.find( - (s) => s.standardId === standardId + (s) => s.standardId === standardId, ); const standardObject = currentTenantObj?.[standardId]; const directStandardValue = standardObject?.Value; @@ -390,10 +416,40 @@ const Page = () => { : null; let isCompliant = false; - // For ConditionalAccessTemplate, the value is true if compliant, or an object with comparison data if not compliant - if (directStandardValue === true) { + // FIRST: Check if CurrentValue and ExpectedValue exist and match + if ( + standardObject?.CurrentValue !== undefined && + standardObject?.ExpectedValue !== undefined + ) { + const sortedCurrent = + typeof standardObject.CurrentValue === "object" && + standardObject.CurrentValue !== null + ? Object.keys(standardObject.CurrentValue) + .sort() + .reduce((obj, key) => { + obj[key] = standardObject.CurrentValue[key]; + return obj; + }, {}) + : standardObject.CurrentValue; + const sortedExpected = + typeof standardObject.ExpectedValue === "object" && + standardObject.ExpectedValue !== null + ? Object.keys(standardObject.ExpectedValue) + .sort() + .reduce((obj, key) => { + obj[key] = standardObject.ExpectedValue[key]; + return obj; + }, {}) + : standardObject.ExpectedValue; + isCompliant = + JSON.stringify(sortedCurrent) === JSON.stringify(sortedExpected); + } + // SECOND: Check if Value is explicitly true + else if (directStandardValue === true) { isCompliant = true; - } else { + } + // THIRD: Otherwise not compliant + else { isCompliant = false; } @@ -423,8 +479,8 @@ const Page = () => { complianceStatus: isOverridden ? "Overridden" : isCompliant - ? "Compliant" - : "Non-Compliant", + ? "Compliant" + : "Non-Compliant", complianceDetails: standardInfo?.docsDescription || standardInfo?.helpText || "", standardDescription: standardInfo?.helpText || "", @@ -461,12 +517,12 @@ const Page = () => { if (itemTemplateId) { const standardId = `standards.ConditionalAccessTemplate.${itemTemplateId}`; const standardInfo = standards.find( - (s) => s.name === `standards.ConditionalAccessTemplate` + (s) => s.name === `standards.ConditionalAccessTemplate`, ); // Find the tenant's value for this specific template const currentTenantStandard = currentTenantData.find( - (s) => s.standardId === standardId + (s) => s.standardId === standardId, ); const standardObject = currentTenantObj?.[standardId]; const directStandardValue = standardObject?.Value; @@ -509,8 +565,8 @@ const Page = () => { complianceStatus: isOverridden ? "Overridden" : isCompliant - ? "Compliant" - : "Non-Compliant", + ? "Compliant" + : "Non-Compliant", complianceDetails: standardInfo?.docsDescription || standardInfo?.helpText || "", standardDescription: standardInfo?.helpText || "", @@ -553,7 +609,7 @@ const Page = () => { // Find the tenant's value for this template const currentTenantStandard = currentTenantData.find( - (s) => s.standardId === standardId + (s) => s.standardId === standardId, ); const standardObject = currentTenantObj?.[standardId]; const directStandardValue = standardObject?.Value; @@ -633,8 +689,8 @@ const Page = () => { complianceStatus: isOverridden ? "Overridden" : isCompliant - ? "Compliant" - : "Non-Compliant", + ? "Compliant" + : "Non-Compliant", complianceDetails: standardInfo?.docsDescription || standardInfo?.helpText || "", standardDescription: standardInfo?.helpText || "", standardImpact: standardInfo?.impact || "Medium Impact", @@ -674,44 +730,119 @@ const Page = () => { actions.filter( (action) => action?.value.toLowerCase() === "report" || - action?.value.toLowerCase() === "remediate" + action?.value.toLowerCase() === "remediate", ).length > 0; // Find the tenant's value for this standard const currentTenantStandard = currentTenantData.find( - (s) => s.standardId === standardId + (s) => s.standardId === standardId, ); // Check if the standard is directly in the tenant object (like "standards.AuditLog": {...}) const standardIdWithoutPrefix = standardId.replace("standards.", ""); const standardObject = currentTenantObj?.[standardId]; + console.log( + "standardId:", + standardId, + "includes IntuneTag:", + standardId.includes("IntuneTag"), + ); + + // Debug logging for Intune tags + if (standardId.includes("IntuneTag") || standardId.includes("intuneTag")) { + console.log(`[${standardId}] standardObject:`, { + standardObject, + hasCurrentValue: standardObject?.CurrentValue !== undefined, + hasExpectedValue: standardObject?.ExpectedValue !== undefined, + Value: standardObject?.Value, + CurrentValue: standardObject?.CurrentValue, + ExpectedValue: standardObject?.ExpectedValue, + }); + } + // Extract the actual value from the standard object (new data structure includes .Value property) const directStandardValue = standardObject?.Value; - // Determine compliance - use backend's logic: Value === true OR CurrentValue === ExpectedValue + // Determine compliance - prioritize Value field, then CurrentValue/ExpectedValue comparison let isCompliant = false; let reportingDisabled = !reportingEnabled; - if (directStandardValue === true) { - // Boolean true means compliant + // Helper function to compare values, handling arrays with order-independent comparison + const compareValues = (val1, val2) => { + // If both are arrays, compare as sets (order-independent) + if (Array.isArray(val1) && Array.isArray(val2)) { + if (val1.length !== val2.length) return false; + // Sort both arrays by their JSON representation for consistent comparison + const sorted1 = [...val1].sort((a, b) => + JSON.stringify(a).localeCompare(JSON.stringify(b)), + ); + const sorted2 = [...val2].sort((a, b) => + JSON.stringify(a).localeCompare(JSON.stringify(b)), + ); + return JSON.stringify(sorted1) === JSON.stringify(sorted2); + } + // For objects, sort keys to ensure consistent comparison + if ( + typeof val1 === "object" && + val1 !== null && + typeof val2 === "object" && + val2 !== null + ) { + const sortedVal1 = Object.keys(val1) + .sort() + .reduce((obj, key) => { + obj[key] = val1[key]; + return obj; + }, {}); + const sortedVal2 = Object.keys(val2) + .sort() + .reduce((obj, key) => { + obj[key] = val2[key]; + return obj; + }, {}); + return JSON.stringify(sortedVal1) === JSON.stringify(sortedVal2); + } + // Otherwise use standard JSON comparison + return JSON.stringify(val1) === JSON.stringify(val2); + }; + + // FIRST: Check if CurrentValue and ExpectedValue exist and match (most reliable) + if ( + standardObject?.CurrentValue !== undefined && + standardObject?.ExpectedValue !== undefined + ) { + isCompliant = compareValues( + standardObject.CurrentValue, + standardObject.ExpectedValue, + ); + // Debug logging for Intune tags + if (standardId.includes("IntuneTag") || standardId.includes("intuneTag")) { + console.log(`[${standardId}] Comparing CurrentValue vs ExpectedValue:`, { + CurrentValue: standardObject.CurrentValue, + ExpectedValue: standardObject.ExpectedValue, + isCompliant, + currentJSON: JSON.stringify(standardObject.CurrentValue), + expectedJSON: JSON.stringify(standardObject.ExpectedValue), + areEqual: + JSON.stringify(standardObject.CurrentValue) === + JSON.stringify(standardObject.ExpectedValue), + }); + } + } + // SECOND: Check if Value is explicitly true (compliant) or false (non-compliant) + else if (directStandardValue === true) { isCompliant = true; - } else if (standardObject?.CurrentValue && standardObject?.ExpectedValue) { - // Compare CurrentValue and ExpectedValue (backend's comparison logic) - isCompliant = - JSON.stringify(standardObject.CurrentValue) === - JSON.stringify(standardObject.ExpectedValue); - } else if (standardObject?.CurrentValue && standardObject?.ExpectedValue) { - // Compare CurrentValue and ExpectedValue (backend's comparison logic) - isCompliant = - JSON.stringify(standardObject.CurrentValue) === - JSON.stringify(standardObject.ExpectedValue); - } else if (directStandardValue !== undefined) { - // For non-boolean values, use strict equality + } else if (directStandardValue === false) { + isCompliant = false; + } + // THIRD: For other non-boolean, non-null values, use strict equality with template settings + else if (directStandardValue !== undefined && directStandardValue !== null) { isCompliant = JSON.stringify(directStandardValue) === JSON.stringify(standardSettings); - } else if (currentTenantStandard) { - // Fall back to the previous logic if the standard is not directly in the tenant object + } + // FOURTH: Fall back to the previous logic if the standard is not directly in the tenant object + else if (currentTenantStandard) { if (typeof standardSettings === "boolean" && standardSettings === true) { isCompliant = currentTenantStandard.value === true; } else { @@ -725,8 +856,8 @@ const Page = () => { const complianceStatus = reportingDisabled ? "Reporting Disabled" : isCompliant - ? "Compliant" - : "Non-Compliant"; + ? "Compliant" + : "Non-Compliant"; // Check if this standard is overridden by another template const tenantTemplateId = standardObject?.TemplateId; @@ -913,14 +1044,14 @@ const Page = () => { const compliancePercentage = allCount > 0 ? Math.round( - (compliantCount / (allCount - reportingDisabledCount - overriddenCount || 1)) * 100 + (compliantCount / (allCount - reportingDisabledCount - overriddenCount || 1)) * 100, ) : 0; const missingLicensePercentage = allCount > 0 ? Math.round( - (missingLicenseCount / (allCount - reportingDisabledCount - overriddenCount || 1)) * 100 + (missingLicenseCount / (allCount - reportingDisabledCount - overriddenCount || 1)) * 100, ) : 0; @@ -1102,7 +1233,7 @@ const Page = () => { query: query, }, undefined, - { shallow: true } + { shallow: true }, ); }} sx={{ width: 300 }} @@ -1198,8 +1329,8 @@ const Page = () => { compliancePercentage === 100 ? "success" : compliancePercentage >= 50 - ? "warning" - : "error" + ? "warning" + : "error" } /> { missingLicensePercentage === 0 ? "success" : missingLicensePercentage <= 25 - ? "warning" - : "error" + ? "warning" + : "error" } /> { standard.complianceStatus === "Compliant" ? "success.main" : standard.complianceStatus === "Overridden" - ? "warning.main" - : standard.complianceStatus === "Reporting Disabled" - ? "grey.500" - : "error.main", + ? "warning.main" + : standard.complianceStatus === "Reporting Disabled" + ? "grey.500" + : "error.main", }} > {standard.complianceStatus === "Compliant" ? ( @@ -1481,78 +1612,94 @@ const Page = () => { - {!standard.standardValue ? ( - - This data has not yet been collected. Collect the data by pressing - the report button on the top of the page. - - ) : ( + {/* Show Expected Configuration with property-by-property breakdown */} + {standard.currentTenantValue?.ExpectedValue !== undefined ? ( - - - {standard.standardValue && - typeof standard.standardValue === "object" && - Object.keys(standard.standardValue).length > 0 ? ( - Object.entries(standard.standardValue).map(([key, value]) => ( - + + Expected Configuration + + {typeof standard.currentTenantValue.ExpectedValue === "object" && + standard.currentTenantValue.ExpectedValue !== null ? ( + + {Object.entries(standard.currentTenantValue.ExpectedValue).map( + ([key, val]) => ( + - {key}: + {key} - - {typeof value === "object" && value !== null - ? value?.label || JSON.stringify(value) - : value === true - ? "Enabled" - : value === false - ? "Disabled" - : String(value)} - + + {val !== undefined + ? JSON.stringify(val, null, 2) + : "Not set"} + + - )) - ) : ( - - {standard.standardValue === true ? ( - - This setting is configured correctly - - ) : standard.standardValue === false ? ( - - This setting is not configured correctly - - ) : standard.standardValue !== undefined ? ( - typeof standard.standardValue === "object" ? ( - "No settings configured" - ) : ( - String(standard.standardValue) - ) - ) : ( - - This setting is not configured, or data has not been - collected. If you are getting this after data - collection, the tenant might not be licensed for this - feature - - )} - + ), )} + + ) : ( + + + {String(standard.currentTenantValue.ExpectedValue)} + - + )} + ) : ( + + This data has not yet been collected. Collect the data by pressing + the report button on the top of the page. + )} @@ -1563,8 +1710,8 @@ const Page = () => { standard.standardImpactColour === "info" ? "info" : standard.standardImpactColour === "warning" - ? "warning" - : "error" + ? "warning" + : "error" } sx={{ mr: 1 }} /> @@ -1629,10 +1776,10 @@ const Page = () => { standard.complianceStatus === "Compliant" ? "success.main" : standard.complianceStatus === "Overridden" - ? "warning.main" - : standard.complianceStatus === "Reporting Disabled" - ? "grey.500" - : "error.main", + ? "warning.main" + : standard.complianceStatus === "Reporting Disabled" + ? "grey.500" + : "error.main", borderRadius: "50%", width: 8, height: 8, @@ -1652,7 +1799,7 @@ const Page = () => { } size="small" label={`${new Date( - standard.currentTenantValue.LastRefresh + standard.currentTenantValue.LastRefresh, ).toLocaleString()}`} variant="outlined" /> @@ -1689,109 +1836,244 @@ const Page = () => { ) : standard.complianceStatus === "Compliant" ? ( <> - - This setting is configured correctly - - - ) : standard.currentTenantValue?.Value === false ? ( - <> - - This setting is not configured correctly - - {/* Show Current/Expected values for non-compliant standards */} - {standard.currentTenantValue?.CurrentValue && - standard.currentTenantValue?.ExpectedValue && ( - - - - Expected - - + {/* Show Current value property-by-property for compliant standards */} + {standard.currentTenantValue?.CurrentValue !== undefined ? ( + typeof standard.currentTenantValue.CurrentValue === + "object" && + standard.currentTenantValue.CurrentValue !== null ? ( + + + Current Configuration + + {Object.entries( + standard.currentTenantValue.CurrentValue, + ).map(([key, val]) => ( + - {JSON.stringify( - standard.currentTenantValue.ExpectedValue, - null, - 2 - )} + {key} + + + + + + {val !== undefined + ? JSON.stringify(val, null, 2) + : "Not set"} + + - - + ))} + + ) : ( + + + Current Configuration + + - Current + {String(standard.currentTenantValue.CurrentValue)} - + + + ) + ) : null} + + ) : ( + <> + {standard.currentTenantValue?.Value === false && ( + + This setting is not configured correctly + + )} + {/* Show Current value property-by-property for non-compliant standards */} + {standard.currentTenantValue?.CurrentValue !== undefined && + (typeof standard.currentTenantValue.CurrentValue === + "object" && + standard.currentTenantValue.CurrentValue !== null ? ( + + + Current Configuration + + {Object.entries( + standard.currentTenantValue.CurrentValue, + ).map(([key, val]) => ( + - {JSON.stringify( - standard.currentTenantValue.CurrentValue, - null, - 2 - )} + {key} + + + {val !== undefined + ? JSON.stringify(val, null, 2) + : "Not set"} + + + ))} + + ) : ( + + + Current Configuration + + + + {String(standard.currentTenantValue.CurrentValue)} + - )} + ))} - ) : null} + )} {/* Only show values if they're not simple true/false that's already covered by the alerts above */} {!( @@ -1810,7 +2092,7 @@ const Page = () => { key === "Value" && (standard.currentTenantValue?.Value === true || standard.currentTenantValue?.Value === false) - ) + ), ) .map(([key, value]) => { const actualValue = key === "Value" ? value : value; @@ -1858,8 +2140,8 @@ const Page = () => { standard.complianceStatus === "Compliant" ? "success.main" : isDifferent - ? "error.main" - : "inherit", + ? "error.main" + : "inherit", fontWeight: standard.complianceStatus === "Non-Compliant" && isDifferent @@ -1902,10 +2184,10 @@ const Page = () => { standard.complianceStatus === "Compliant" ? "success.main" : standard.complianceStatus === "Overridden" - ? "warning.main" - : standard.complianceStatus === "Reporting Disabled" - ? "text.secondary" - : "error.main", + ? "warning.main" + : standard.complianceStatus === "Reporting Disabled" + ? "text.secondary" + : "error.main", fontWeight: standard.complianceStatus === "Non-Compliant" ? "medium" @@ -1924,26 +2206,265 @@ const Page = () => { standard.overridingTemplateId} ) : standard.complianceStatus === "Compliant" ? ( - - This setting is configured correctly - - ) : standard.currentTenantValue?.Value === false || - standard.currentTenantValue === false ? ( - - This setting is not configured correctly - - ) : standard.currentTenantValue !== undefined ? ( - String( - standard.currentTenantValue?.Value !== undefined - ? standard.currentTenantValue?.Value - : standard.currentTenantValue - ) + <> + {/* Show Current value property-by-property in card view */} + {standard.currentTenantValue?.CurrentValue !== undefined ? ( + typeof standard.currentTenantValue.CurrentValue === + "object" && + standard.currentTenantValue.CurrentValue !== null ? ( + + + Current Configuration + + {Object.entries( + standard.currentTenantValue.CurrentValue, + ).map(([key, val]) => ( + + + {key} + + + + + + + {val !== undefined + ? JSON.stringify(val, null, 2) + : "Not set"} + + + + ))} + + ) : ( + + + Current Configuration + + + + {String(standard.currentTenantValue.CurrentValue)} + + + + ) + ) : null} + ) : ( - - This setting is not configured, or data has not been collected. - If you are getting this after data collection, the tenant might - not be licensed for this feature - + <> + {(standard.currentTenantValue?.Value === false || + standard.currentTenantValue === false) && ( + + This setting is not configured correctly + + )} + {/* Show Current value property-by-property for non-compliant standards in card view */} + {standard.currentTenantValue?.CurrentValue !== undefined ? ( + typeof standard.currentTenantValue.CurrentValue === + "object" && + standard.currentTenantValue.CurrentValue !== null ? ( + + + Current Configuration + + {Object.entries( + standard.currentTenantValue.CurrentValue, + ).map(([key, val]) => ( + + + {key} + + + + {val !== undefined + ? JSON.stringify(val, null, 2) + : "Not set"} + + + + ))} + + ) : ( + + + Current Configuration + + + + {String(standard.currentTenantValue.CurrentValue)} + + + + ) + ) : standard.currentTenantValue !== undefined && + standard.currentTenantValue?.Value !== true && + standard.currentTenantValue?.Value !== false ? ( + + {String( + standard.currentTenantValue?.Value !== undefined + ? standard.currentTenantValue?.Value + : standard.currentTenantValue, + )} + + ) : standard.currentTenantValue === undefined || + (standard.currentTenantValue?.Value === null && + standard.currentTenantValue?.CurrentValue === undefined && + standard.currentTenantValue?.ExpectedValue === + undefined) ? ( + + This setting is not configured, or data has not been + collected. If you are getting this after data collection, + the tenant might not be licensed for this feature + + ) : null} + )} )} From a9d0c78d34311d86cd56c037009f26775af31627 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Tue, 20 Jan 2026 15:14:07 -0500 Subject: [PATCH 5/5] Add tenant region display to lookup page Displays the tenant region using OpenIdConfig. Also updates the fallback text for tenant brand name from 'N/A' to 'Not Specified'. --- src/pages/tenant/tools/tenantlookup/index.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/pages/tenant/tools/tenantlookup/index.js b/src/pages/tenant/tools/tenantlookup/index.js index 1678f428f818..08453771819d 100644 --- a/src/pages/tenant/tools/tenantlookup/index.js +++ b/src/pages/tenant/tools/tenantlookup/index.js @@ -84,7 +84,11 @@ const Page = () => { Tenant Brand Name :{" "} {getTenant.data?.GraphRequest?.federationBrandName ? getTenant.data?.GraphRequest?.federationBrandName - : "N/A"} + : "Not Specified"} + + + Tenant Region:{" "} + {getTenant.data?.OpenIdConfig?.tenant_region_scope}