diff --git a/public/version.json b/public/version.json index c0ab80971e47..ba547756572f 100644 --- a/public/version.json +++ b/public/version.json @@ -1,3 +1,3 @@ { - "version": "7.3.1" + "version": "7.3.2" } diff --git a/src/components/CippComponents/CippAutocomplete.jsx b/src/components/CippComponents/CippAutocomplete.jsx index 94b71886d005..4fb8c073086c 100644 --- a/src/components/CippComponents/CippAutocomplete.jsx +++ b/src/components/CippComponents/CippAutocomplete.jsx @@ -99,7 +99,7 @@ export const CippAutoComplete = (props) => { setGetRequestInfo({ url: api.url, data: { - ...(!api.excludeTenantFilter ? { TenantFilter: currentTenant } : null), + ...(!api.excludeTenantFilter ? { tenantFilter: currentTenant } : null), ...api.data, }, waiting: true, diff --git a/src/components/CippComponents/CippExchangeActions.jsx b/src/components/CippComponents/CippExchangeActions.jsx new file mode 100644 index 000000000000..329fb3492eed --- /dev/null +++ b/src/components/CippComponents/CippExchangeActions.jsx @@ -0,0 +1,240 @@ +import { + EyeIcon, + TrashIcon, + MagnifyingGlassIcon, + PlayCircleIcon, +} from "@heroicons/react/24/outline"; +import { + Archive, + MailOutline, + Person, + Room, + Visibility, + VisibilityOff, + PhonelinkLock, + Key, + PostAdd, + Add, +} from "@mui/icons-material"; +import { useSettings } from "/src/hooks/use-settings.js"; + +export const CippExchangeActions = () => { + // const tenant = useSettings().currentTenant; + return [ + { + label: "Edit permissions", + link: "/identity/administration/users/user/exchange?userId=[ExternalDirectoryObjectId]", + color: "info", + icon: , + }, + { + label: "Research Compromised Account", + link: "/identity/administration/users/user/bec?userId=[ExternalDirectoryObjectId]", + color: "info", + icon: , + }, + { + label: "Send MFA Push", + type: "POST", + url: "/api/ExecSendPush", + data: { + UserEmail: "UPN", + }, + confirmText: "Are you sure you want to send an MFA request?", + icon: , + }, + { + label: "Convert to User Mailbox", + type: "POST", + url: "/api/ExecConvertMailbox", + icon: , + data: { + ID: "UPN", + MailboxType: "!Regular", + }, + confirmText: "Are you sure you want to convert this mailbox to a user mailbox?", + condition: (row) => row.recipientTypeDetails !== "UserMailbox", + }, + { + label: "Convert to Shared Mailbox", + type: "POST", + icon: , + url: "/api/ExecConvertMailbox", + data: { + ID: "UPN", + MailboxType: "!Shared", + }, + confirmText: "Are you sure you want to convert this mailbox to a shared mailbox?", + condition: (row) => row.recipientTypeDetails !== "SharedMailbox", + }, + { + label: "Convert to Room Mailbox", + type: "POST", + url: "/api/ExecConvertMailbox", + icon: , + data: { + ID: "UPN", + MailboxType: "!Room", + }, + confirmText: "Are you sure you want to convert this mailbox to a room mailbox?", + condition: (row) => row.recipientTypeDetails !== "RoomMailbox", + }, + { + //tested + label: "Enable Online Archive", + type: "POST", + icon: , + url: "/api/ExecEnableArchive", + data: { ID: "Id", username: "UPN" }, + confirmText: "Are you sure you want to enable the online archive for this user?", + multiPost: false, + condition: (row) => row.ArchiveGuid === "00000000-0000-0000-0000-000000000000", + }, + { + label: "Enable Auto-Expanding Archive", + type: "POST", + icon: , + url: "/api/ExecEnableAutoExpandingArchive", + data: { ID: "Id", username: "UPN" }, + confirmText: + "Are you sure you want to enable auto-expanding archive for this user? The archive must already be enabled.", + multiPost: false, + condition: (row) => row.ArchiveGuid !== "00000000-0000-0000-0000-000000000000", + }, + { + label: "Hide from Global Address List", + type: "POST", + url: "/api/ExecHideFromGAL", + icon: , + data: { + ID: "UPN", + HidefromGAL: true, + }, + confirmText: + "Are you sure you want to hide this mailbox from the global address list? This will not work if the user is AD Synced.", + condition: (row) => row.HiddenFromAddressListsEnabled === false, + }, + { + label: "Unhide from Global Address List", + type: "POST", + url: "/api/ExecHideFromGAL", + icon: , + data: { + ID: "UPN", + HidefromGAL: false, + }, + confirmText: + "Are you sure you want to unhide this mailbox from the global address list? This will not work if the user is AD Synced.", + condition: (row) => row.HiddenFromAddressListsEnabled === true, + }, + { + label: "Start Managed Folder Assistant", + type: "POST", + url: "/api/ExecStartManagedFolderAssistant", + icon: , + data: { + ID: "ExchangeGuid", + UserPrincipalName: "UPN", + }, + confirmText: "Are you sure you want to start the managed folder assistant for this user?", + }, + { + label: "Delete Mailbox", + type: "POST", + icon: , + url: "/api/RemoveUser", + data: { ID: "UPN" }, + confirmText: "Are you sure you want to delete this mailbox?", + multiPost: false, + }, + { + label: "Copy Sent Items to Shared Mailbox", + type: "POST", + url: "/api/ExecCopyForSent", + data: { ID: "UPN" }, + confirmText: "Are you sure you want to enable Copy Sent Items to Shared Mailbox?", + icon: , + condition: (row) => + row.MessageCopyForSentAsEnabled === false && row.recipientTypeDetails === "SharedMailbox", + }, + { + label: "Disable Copy Sent Items to Shared Mailbox", + type: "POST", + url: "/api/ExecCopyForSent", + data: { ID: "UPN", MessageCopyForSentAsEnabled: false }, + confirmText: "Are you sure you want to disable Copy Sent Items to Shared Mailbox?", + icon: , + condition: (row) => + row.MessageCopyForSentAsEnabled === true && row.recipientTypeDetails === "SharedMailbox", + }, + { + label: "Set mailbox locale", + type: "POST", + url: "/api/ExecSetMailboxLocale", + data: { user: "UPN", ProhibitSendQuota: true }, + confirmText: "Enter a locale, e.g. en-US", + icon: , + fields: [ + { + label: "Locale", + name: "locale", + type: "textField", + placeholder: "e.g. en-US", + }, + ], + }, + { + label: "Set Send Quota", + type: "POST", + url: "/api/ExecSetMailboxQuota", + data: { user: "UPN", ProhibitSendQuota: true }, + confirmText: "Enter a quota. e.g. 1000MB, 10GB,1TB", + icon: , + fields: [ + { + label: "Quota", + name: "quota", + type: "textField", + placeholder: "e.g. 1000MB, 10GB,1TB", + }, + ], + }, + { + label: "Set Send and Receive Quota", + type: "POST", + url: "/api/ExecSetMailboxQuota", + data: { + user: "UPN", + ProhibitSendReceiveQuota: true, + }, + confirmText: "Enter a quota. e.g. 1000MB, 10GB,1TB", + icon: , + fields: [ + { + label: "Quota", + name: "quota", + type: "textField", + placeholder: "e.g. 1000MB, 10GB,1TB", + }, + ], + }, + { + label: "Set Quota Warning Level", + type: "POST", + url: "/api/ExecSetMailboxQuota", + data: { user: "UPN", IssueWarningQuota: true }, + confirmText: "Enter a quota. e.g. 1000MB, 10GB,1TB", + icon: , + fields: [ + { + label: "Quota", + name: "quota", + type: "textField", + placeholder: "e.g. 1000MB, 10GB,1TB", + }, + ], + }, + ]; +}; + +export default CippExchangeActions; diff --git a/src/components/CippFormPages/CippAddEditUser.jsx b/src/components/CippFormPages/CippAddEditUser.jsx index 5087d40456fa..9cee72637872 100644 --- a/src/components/CippFormPages/CippAddEditUser.jsx +++ b/src/components/CippFormPages/CippAddEditUser.jsx @@ -269,7 +269,7 @@ const CippAddEditUser = (props) => { formControl={formControl} /> - {userSettingsDefaults?.userAttributes?.map((attribute, idx) => ( + {userSettingsDefaults?.userAttributes?.filter((attribute) => attribute.value !== "sponsor").map((attribute, idx) => ( { ); }, + enableGlobalFilterModes: true, }); useEffect(() => { diff --git a/src/layouts/config.js b/src/layouts/config.js index e7093b9f1883..812b5639f999 100644 --- a/src/layouts/config.js +++ b/src/layouts/config.js @@ -463,7 +463,7 @@ export const nativeMenuItems = [ ), items: [ { title: "Application Settings", path: "/cipp/settings", roles: ["admin", "superadmin"] }, - { title: "Logbook", path: "/cipp/logs", roles: ["admin", "superadmin"] }, + { title: "Logbook", path: "/cipp/logs", roles: ["editor", "admin", "superadmin"] }, { title: "SAM Setup Wizard", path: "/onboarding", roles: ["admin", "superadmin"] }, { title: "Integrations", path: "/cipp/integrations", roles: ["admin", "superadmin"] }, { diff --git a/src/layouts/index.js b/src/layouts/index.js index fa6e9812d5b6..c2f49483f74a 100644 --- a/src/layouts/index.js +++ b/src/layouts/index.js @@ -231,7 +231,7 @@ export const Layout = (props) => { }} > - {currentTenant === "AllTenants" && !allTenantsSupport ? ( + {(currentTenant === "AllTenants" || !currentTenant) && !allTenantsSupport ? ( diff --git a/src/pages/cipp/preferences.js b/src/pages/cipp/preferences.js index 81fd6d446bbf..40ccac234133 100644 --- a/src/pages/cipp/preferences.js +++ b/src/pages/cipp/preferences.js @@ -35,6 +35,7 @@ const Page = () => { { value: "otherMails", label: "otherMails" }, { value: "showInAddressList", label: "showInAddressList" }, { value: "state", label: "state" }, + { value: "city", label: "city" }, { value: "sponsor", label: "sponsor" }, ]; diff --git a/src/pages/cipp/scheduler/index.js b/src/pages/cipp/scheduler/index.js index 97abcc20b602..81cf3213ed02 100644 --- a/src/pages/cipp/scheduler/index.js +++ b/src/pages/cipp/scheduler/index.js @@ -82,19 +82,20 @@ const Page = () => { tenantInTitle={false} title="Scheduled Tasks" apiUrl={ - showHiddenJobs ? "/api/ListScheduledItems?ListHidden=True" : "/api/ListScheduledItems" + showHiddenJobs ? "/api/ListScheduledItems?ShowHidden=true" : "/api/ListScheduledItems" } queryKey={showHiddenJobs ? `ListScheduledItems-hidden` : `ListScheduledItems`} simpleColumns={[ - "Name", - "Tenant", + "ExecutedTime", "TaskState", + "Tenant", + "Name", + "ScheduledTime", "Command", "Parameters", "PostExecution", "Recurrence", - "ExecutedTime", - "ScheduledTime", + "Results", ]} actions={actions} offCanvas={offCanvas} diff --git a/src/pages/email/administration/mailboxes/index.js b/src/pages/email/administration/mailboxes/index.js index c4014ca25d38..da5479b87fb4 100644 --- a/src/pages/email/administration/mailboxes/index.js +++ b/src/pages/email/administration/mailboxes/index.js @@ -2,243 +2,16 @@ import { Layout as DashboardLayout } from "/src/layouts/index.js"; import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; import Link from "next/link"; import { Button } from "@mui/material"; -import { - Archive, - MailOutline, - Person, - Room, - Visibility, - VisibilityOff, - PhonelinkLock, - Key, - PostAdd, - Add, -} from "@mui/icons-material"; -import { TrashIcon, MagnifyingGlassIcon, PlayCircleIcon } from "@heroicons/react/24/outline"; +import { Add } from "@mui/icons-material"; +import CippExchangeActions from "../../../../components/CippComponents/CippExchangeActions"; const Page = () => { const pageTitle = "Mailboxes"; - // Define actions for mailboxes - const actions = [ - { - label: "Edit permissions", - link: "/identity/administration/users/user/exchange?userId=[ExternalDirectoryObjectId]", - color: "info", - icon: , - }, - { - label: "Research Compromised Account", - link: "/identity/administration/users/user/bec?userId=[ExternalDirectoryObjectId]", - color: "info", - icon: , - }, - { - label: "Send MFA Push", - type: "POST", - url: "/api/ExecSendPush", - data: { - UserEmail: "UPN", - }, - confirmText: "Are you sure you want to send an MFA request?", - icon: , - }, - { - label: "Convert to User Mailbox", - type: "POST", - url: "/api/ExecConvertMailbox", - icon: , - data: { - ID: "UPN", - MailboxType: "!Regular", - }, - confirmText: "Are you sure you want to convert this mailbox to a user mailbox?", - condition: (row) => row.recipientTypeDetails !== "UserMailbox", - }, - { - label: "Convert to Shared Mailbox", - type: "POST", - icon: , - url: "/api/ExecConvertMailbox", - data: { - ID: "UPN", - MailboxType: "!Shared", - }, - confirmText: "Are you sure you want to convert this mailbox to a shared mailbox?", - condition: (row) => row.recipientTypeDetails !== "SharedMailbox", - }, - { - label: "Convert to Room Mailbox", - type: "POST", - url: "/api/ExecConvertMailbox", - icon: , - data: { - ID: "UPN", - MailboxType: "!Room", - }, - confirmText: "Are you sure you want to convert this mailbox to a room mailbox?", - condition: (row) => row.recipientTypeDetails !== "RoomMailbox", - }, - { - //tested - label: "Enable Online Archive", - type: "POST", - icon: , - url: "/api/ExecEnableArchive", - data: { ID: "Id", username: "UPN" }, - confirmText: "Are you sure you want to enable the online archive for this user?", - multiPost: false, - condition: (row) => row.ArchiveGuid === "00000000-0000-0000-0000-000000000000", - }, - { - label: "Enable Auto-Expanding Archive", - type: "POST", - icon: , - url: "/api/ExecEnableAutoExpandingArchive", - data: { ID: "Id", username: "UPN" }, - confirmText: - "Are you sure you want to enable auto-expanding archive for this user? The archive must already be enabled.", - multiPost: false, - condition: (row) => row.ArchiveGuid !== "00000000-0000-0000-0000-000000000000", - }, - { - label: "Hide from Global Address List", - type: "POST", - url: "/api/ExecHideFromGAL", - icon: , - data: { - ID: "UPN", - HidefromGAL: true, - }, - confirmText: - "Are you sure you want to hide this mailbox from the global address list? This will not work if the user is AD Synced.", - condition: (row) => row.HiddenFromAddressListsEnabled === false, - }, - { - label: "Unhide from Global Address List", - type: "POST", - url: "/api/ExecHideFromGAL", - icon: , - data: { - ID: "UPN", - }, - confirmText: - "Are you sure you want to unhide this mailbox from the global address list? This will not work if the user is AD Synced.", - condition: (row) => row.HiddenFromAddressListsEnabled === true, - }, - { - label: "Start Managed Folder Assistant", - type: "POST", - url: "/api/ExecStartManagedFolderAssistant", - icon: , - data: { - ID: "ExchangeGuid", - UserPrincipalName: "UPN", - }, - confirmText: "Are you sure you want to start the managed folder assistant for this user?", - }, - { - label: "Delete Mailbox", - type: "POST", - icon: , - url: "/api/RemoveUser", - data: { ID: "UPN" }, - confirmText: "Are you sure you want to delete this mailbox?", - multiPost: false, - }, - { - label: "Copy Sent Items to Shared Mailbox", - type: "POST", - url: "/api/ExecCopyForSent", - data: { ID: "UPN" }, - confirmText: "Are you sure you want to enable Copy Sent Items to Shared Mailbox?", - icon: , - condition: (row) => - row.MessageCopyForSentAsEnabled === false && row.recipientTypeDetails === "SharedMailbox", - }, - { - label: "Disable Copy Sent Items to Shared Mailbox", - type: "POST", - url: "/api/ExecCopyForSent", - data: { ID: "UPN", MessageCopyForSentAsEnabled: false }, - confirmText: "Are you sure you want to disable Copy Sent Items to Shared Mailbox?", - icon: , - condition: (row) => - row.MessageCopyForSentAsEnabled === true && row.recipientTypeDetails === "SharedMailbox", - }, - { - label: "Set mailbox locale", - type: "POST", - url: "/api/ExecSetMailboxLocale", - data: { user: "UPN", ProhibitSendQuota: true }, - confirmText: "Enter a locale, e.g. en-US", - icon: , - fields: [ - { - label: "Locale", - name: "locale", - type: "textField", - placeholder: "e.g. en-US", - }, - ], - }, - { - label: "Set Send Quota", - type: "POST", - url: "/api/ExecSetMailboxQuota", - data: { user: "UPN", ProhibitSendQuota: true }, - confirmText: "Enter a quota. e.g. 1000MB, 10GB,1TB", - icon: , - fields: [ - { - label: "Quota", - name: "quota", - type: "textField", - placeholder: "e.g. 1000MB, 10GB,1TB", - }, - ], - }, - { - label: "Set Send and Receive Quota", - type: "POST", - url: "/api/ExecSetMailboxQuota", - data: { - user: "UPN", - ProhibitSendReceiveQuota: true, - }, - confirmText: "Enter a quota. e.g. 1000MB, 10GB,1TB", - icon: , - fields: [ - { - label: "Quota", - name: "quota", - type: "textField", - placeholder: "e.g. 1000MB, 10GB,1TB", - }, - ], - }, - { - label: "Set Quota Warning Level", - type: "POST", - url: "/api/ExecSetMailboxQuota", - data: { user: "UPN", IssueWarningQuota: true }, - confirmText: "Enter a quota. e.g. 1000MB, 10GB,1TB", - icon: , - fields: [ - { - label: "Quota", - name: "quota", - type: "textField", - placeholder: "e.g. 1000MB, 10GB,1TB", - }, - ], - }, - ]; - // Define off-canvas details const offCanvas = { extendedInfoFields: ["displayName", "UPN", "AdditionalEmailAddresses", "recipientTypeDetails"], - actions: actions, + actions: CippExchangeActions(), }; const filterList = [ @@ -278,7 +51,7 @@ const Page = () => { { const userSettingsDefaults = useSettings(); @@ -261,6 +262,8 @@ const Page = () => { tabOptions={tabOptions} title={title} subtitle={subtitle} + actions={CippExchangeActions()} + actionsData={userRequest.data?.[0]?.MailboxActionsData} isFetching={graphUserRequest.isLoading} > diff --git a/src/pages/identity/administration/users/user/index.jsx b/src/pages/identity/administration/users/user/index.jsx index 0d8628fdd9d5..b36347b4a1f3 100644 --- a/src/pages/identity/administration/users/user/index.jsx +++ b/src/pages/identity/administration/users/user/index.jsx @@ -1,7 +1,7 @@ import { Layout as DashboardLayout } from "/src/layouts/index.js"; import { useSettings } from "/src/hooks/use-settings"; import { useRouter } from "next/router"; -import { ApiGetCall } from "/src/api/ApiCall"; +import { ApiGetCall, ApiPostCall } from "/src/api/ApiCall"; import CippFormSkeleton from "/src/components/CippFormPages/CippFormSkeleton"; import CalendarIcon from "@heroicons/react/24/outline/CalendarIcon"; import { AdminPanelSettings, Check, Group, Mail, Fingerprint, Launch } from "@mui/icons-material"; @@ -24,6 +24,7 @@ const CippMap = dynamic(() => import("/src/components/CippComponents/CippMap"), import { Button, Dialog, DialogTitle, DialogContent, IconButton } from "@mui/material"; import { Close } from "@mui/icons-material"; import { CippPropertyList } from "../../../../../components/CippComponents/CippPropertyList"; +import { CippCodeBlock } from "../../../../../components/CippComponents/CippCodeBlock"; const SignInLogsDialog = ({ open, onClose, userId, tenantFilter }) => { return ( @@ -85,33 +86,47 @@ const Page = () => { waiting: waiting, }); - const userMemberOf = ApiGetCall({ - url: "/api/ListGraphRequest", - data: { - Endpoint: `/users/${userId}/memberOf`, - tenantFilter: userSettingsDefaults.currentTenant, - $top: 99, - }, - queryKey: `UserMemberOf-${userId}`, + const userBulkRequest = ApiPostCall({ + urlfromdata: true, }); - const MFARequest = ApiGetCall({ - url: "/api/ListGraphRequest", - data: { - Endpoint: `/users/${userId}/authentication/methods`, - tenantFilter: userSettingsDefaults.currentTenant, - noPagination: true, - $top: 99, - }, - queryKey: `MFA-${userId}`, - waiting: waiting, - }); + useEffect(() => { + if (userId && userSettingsDefaults.currentTenant && !userBulkRequest.isSuccess) { + userBulkRequest.mutate({ + url: "/api/ListGraphBulkRequest", + data: { + Requests: [ + { + id: "userMemberOf", + url: `/users/${userId}/memberOf`, + method: "GET", + }, + { + id: "mfaDevices", + url: `/users/${userId}/authentication/methods?$top=99`, + method: "GET", + }, + { + id: "signInLogs", + url: `/auditLogs/signIns?$filter=(userId eq '${userId}')&$top=1`, + method: "GET", + }, + ], + tenantFilter: userSettingsDefaults.currentTenant, + noPaginateIds: ["signInLogs"], + }, + }); + } + }, [userId, userSettingsDefaults.currentTenant, userBulkRequest.isSuccess]); - const signInLogs = ApiGetCall({ - url: `/api/ListUserSigninLogs?UserId=${userId}&tenantFilter=${userSettingsDefaults.currentTenant}&top=1`, - queryKey: `ListSignIns-${userId}`, - waiting: waiting, - }); + const bulkData = userBulkRequest?.data?.data ?? []; + const signInLogsData = bulkData?.find((item) => item.id === "signInLogs"); + const userMemberOfData = bulkData?.find((item) => item.id === "userMemberOf"); + const mfaDevicesData = bulkData?.find((item) => item.id === "mfaDevices"); + + const signInLogs = signInLogsData?.body?.value || []; + const userMemberOf = userMemberOfData?.body?.value || []; + const mfaDevices = mfaDevicesData?.body?.value || []; // Set the title and subtitle for the layout const title = userRequest.isSuccess ? <>{userRequest.data?.[0]?.displayName} : "Loading..."; @@ -138,15 +153,15 @@ const Page = () => { icon: , text: ( + color="muted" + style={{ paddingLeft: 0 }} + size="small" + href={`https://entra.microsoft.com/${userSettingsDefaults.currentTenant}/#view/Microsoft_AAD_UsersAndTenants/UserProfileMenuBlade/~/overview/userId/${userId}`} + target="_blank" + rel="noopener noreferrer" + > + View in Entra + ), }, ] @@ -159,8 +174,8 @@ const Page = () => { let conditionalAccessPoliciesItems = []; let mfaDevicesItems = []; - if (signInLogs.isSuccess && signInLogs.data && signInLogs.data.length > 0) { - const signInData = signInLogs.data[0]; + if (signInLogs.length > 0) { + const signInData = signInLogs[0]; signInLogItem = { id: 1, @@ -330,12 +345,12 @@ const Page = () => { }, ]; } - } else if (signInLogs.isError) { + } else if (signInLogsData?.status !== 200) { signInLogItem = { id: 1, cardLabelBox: "!", text: "Error loading sign-in logs. Do you have a P1 license?", - subtext: signInLogs.error.message, + subtext: signInLogsData?.error?.message || "Unknown error", statusColor: "error.main", statusText: "Error", propertyItems: [], @@ -347,13 +362,13 @@ const Page = () => { id: 1, cardLabelBox: "!", text: "Error loading conditional access policies. Do you have a P1 license?", - subtext: signInLogs.error.message, + subtext: signInLogsData?.error?.message || "Unknown error", statusColor: "error.main", statusText: "Error", propertyItems: [], }, ]; - } else if (signInLogs.isSuccess && (!signInLogs.data || signInLogs.data.length === 0)) { + } else if (signInLogs.length === 0) { signInLogItem = { id: 1, cardLabelBox: "-", @@ -362,7 +377,21 @@ const Page = () => { "There are no sign-in logs for this user, or you do not have a P1 license to detect this data.", statusColor: "warning.main", statusText: "No Data", - propertyItems: [], + propertyItems: [ + { + label: "Error", + value: signInLogsData?.error?.message || "Unknown error", + }, + { + label: "Inner Error", + value: ( + + ), + }, + ], }; conditionalAccessPoliciesItems = [ @@ -380,16 +409,14 @@ const Page = () => { } // Prepare MFA devices items - if (MFARequest.isSuccess && MFARequest.data) { - const mfaResults = MFARequest.data.Results || []; - + if (mfaDevices.length > 0) { // Exclude password authentication method - const mfaDevices = mfaResults.filter( + const mfaDevicesFiltered = mfaDevices.filter( (method) => method["@odata.type"] !== "#microsoft.graph.passwordAuthenticationMethod" ); - if (mfaDevices.length > 0) { - mfaDevicesItems = mfaDevices.map((device, index) => ({ + if (mfaDevicesFiltered.length > 0) { + mfaDevicesItems = mfaDevicesFiltered.map((device, index) => ({ id: index, cardLabelBox: { cardLabelBoxHeader: , @@ -433,20 +460,37 @@ const Page = () => { }, ]; } - } else if (MFARequest.isError) { + } else if (mfaDevicesData?.status !== 200) { // Error fetching MFA devices mfaDevicesItems = [ { id: 1, cardLabelBox: "!", text: "Error loading MFA devices", - subtext: MFARequest.error.message, + subtext: `Status code: ${mfaDevicesData?.status}`, statusColor: "error.main", statusText: "Error", - propertyItems: [], + propertyItems: [ + { + label: "Error", + value: mfaDevicesData?.body?.error?.message || "Unknown Error", + }, + { + label: "Inner Error", + value: ( + + ), + }, + ], }, ]; - } else if (MFARequest.isSuccess && (!MFARequest.data || !MFARequest.data.Results)) { + } else if (mfaDevices.length === 0) { // No MFA devices data available mfaDevicesItems = [ { @@ -461,7 +505,7 @@ const Page = () => { ]; } - const groupMembershipItems = userMemberOf.isSuccess + const groupMembershipItems = userMemberOf ? [ { id: 1, @@ -480,7 +524,7 @@ const Page = () => { link: "/identity/administration/groups/edit?groupId=[id]", }, ], - data: userMemberOf?.data?.Results.filter( + data: userMemberOf?.filter( (item) => item?.["@odata.type"] === "#microsoft.graph.group" ), simpleColumns: ["displayName", "groupTypes", "securityEnabled", "mailEnabled"], @@ -489,7 +533,7 @@ const Page = () => { ] : []; - const roleMembershipItems = userMemberOf.isSuccess + const roleMembershipItems = userMemberOf ? [ { id: 1, @@ -501,7 +545,7 @@ const Page = () => { table: { title: "Admin Roles", hideTitle: true, - data: userMemberOf?.data?.Results.filter( + data: userMemberOf?.filter( (item) => item?.["@odata.type"] === "#microsoft.graph.directoryRole" ), simpleColumns: ["displayName", "description"], @@ -539,30 +583,30 @@ const Page = () => { Latest Logon Applied Conditional Access Policies 0 ? true : false} /> Multi-Factor Authentication Devices 0 ? true : false} /> Memberships 0 ? true : false} /> 0 ? true : false} /> diff --git a/src/pages/tenant/backup/backup-wizard/add.jsx b/src/pages/tenant/backup/backup-wizard/add.jsx index 90d58127a11a..86ec994ca6c4 100644 --- a/src/pages/tenant/backup/backup-wizard/add.jsx +++ b/src/pages/tenant/backup/backup-wizard/add.jsx @@ -1,12 +1,12 @@ import React from "react"; import { Grid, Typography } from "@mui/material"; import { useForm } from "react-hook-form"; +import { omit } from "lodash"; import { Layout as DashboardLayout } from "/src/layouts/index.js"; import CippFormPage from "/src/components/CippFormPages/CippFormPage"; import CippFormComponent from "/src/components/CippComponents/CippFormComponent"; import { useSettings } from "/src/hooks/use-settings"; import { CippFormTenantSelector } from "../../../../components/CippComponents/CippFormTenantSelector"; -import { te } from "date-fns/locale"; const CreateBackup = () => { const userSettingsDefaults = useSettings(); @@ -26,7 +26,6 @@ const CreateBackup = () => { antiphishing: true, CippWebhookAlerts: true, CippScriptedAlerts: true, - CippStandards: true, }, }); @@ -45,7 +44,7 @@ const CreateBackup = () => { TenantFilter: tenantFilter, Name: `CIPP Backup - ${tenantFilter}`, Command: { value: `New-CIPPBackup` }, - Parameters: { backupType: "Scheduled", ScheduledBackupValues: { ...values } }, + Parameters: { backupType: "Scheduled", ScheduledBackupValues: { ...omit(values, ['tenantFilter']) } }, ScheduledTime: unixTime, Recurrence: { value: "1d" }, }; @@ -168,14 +167,6 @@ const CreateBackup = () => { formControl={formControl} /> - - - {/* Add an empty Grid item to fill the second column */} diff --git a/src/pages/tenant/backup/backup-wizard/restore.jsx b/src/pages/tenant/backup/backup-wizard/restore.jsx index 2a332140b139..d7d8c1f16c37 100644 --- a/src/pages/tenant/backup/backup-wizard/restore.jsx +++ b/src/pages/tenant/backup/backup-wizard/restore.jsx @@ -1,11 +1,12 @@ import React, { useState, useEffect } from "react"; -import { Alert, Grid, Typography } from "@mui/material"; +import { Alert, Divider, Grid, Typography } from "@mui/material"; import { useForm, useWatch } from "react-hook-form"; import { Layout as DashboardLayout } from "/src/layouts/index.js"; import CippFormPage from "/src/components/CippFormPages/CippFormPage"; import CippFormComponent from "/src/components/CippComponents/CippFormComponent"; import { useSettings } from "/src/hooks/use-settings"; import { CippFormCondition } from "/src/components/CippComponents/CippFormCondition"; +import { Chip, Stack } from "@mui/material"; const RestoreBackupForm = () => { const userSettingsDefaults = useSettings(); @@ -85,7 +86,6 @@ const RestoreBackupForm = () => { antiphishing: values.antiphishing, CippWebhookAlerts: values.CippWebhookAlerts, CippScriptedAlerts: values.CippScriptedAlerts, - CippStandards: values.CippStandards, overwrite: values.overwrite, }, }, @@ -95,6 +95,7 @@ const RestoreBackupForm = () => { Email: values.email, PSA: values.psa, }, + DisallowDuplicateName: true, }; return shippedValues; }} @@ -104,7 +105,7 @@ const RestoreBackupForm = () => { Use this form to restore a backup for a tenant. Please select the tenant, backup, and restore options. - + {/* Backup Selector */} { name="backup" multiple={false} api={{ - tenantFilter: tenantFilter, url: "/api/ExecListBackup", queryKey: `BackupList-${tenantFilter}`, - labelField: (option) => `${option.RowKey}`, - valueField: "RowKey", + labelField: (option) => { + const match = option.BackupName.match(/.*_(\d{4}-\d{2}-\d{2})-(\d{2})(\d{2})/); + return match ? `${match[1]} @ ${match[2]}:${match[3]}` : option.BackupName; + }, + valueField: "BackupName", data: { Type: "Scheduled", - TenantFilter: tenantFilter, + NameOnly: true, }, }} formControl={formControl} + required={true} + validators={{ + validate: (value) => !!value || "Please select a backup", + }} /> @@ -210,12 +217,6 @@ const RestoreBackupForm = () => { name="CippScriptedAlerts" formControl={formControl} /> - {/* Overwrite Existing Entries */} @@ -262,38 +263,46 @@ const RestoreBackupForm = () => { - + + + {/* Review and Confirm */} - Review and Confirm - + Review and Confirm + Please review the selected options before submitting. Selected Tenant: - {tenantFilter} + + {tenantFilter} + Selected Backup: - + {formControl.watch("backup")?.label || "None selected"} Overwrite Existing Configuration: - {formControl.watch("overwrite") ? "Yes" : "No"} + + {formControl.watch("overwrite") ? "Yes" : "No"} + + Send Results To: - - {formControl.watch("webhook") && "Webhook "} - {formControl.watch("email") && "E-mail "} - {formControl.watch("psa") && "PSA "} - {!formControl.watch("webhook") && - !formControl.watch("email") && - !formControl.watch("psa") && - "None"} + + + {formControl.watch("webhook") && } + {formControl.watch("email") && } + {formControl.watch("psa") && } + {!formControl.watch("webhook") && + !formControl.watch("email") && + !formControl.watch("psa") && } + diff --git a/src/pages/tenant/reports/list-csp-licenses/index.jsx b/src/pages/tenant/reports/list-csp-licenses/index.jsx index 9e6264df4c74..b046b6ea275c 100644 --- a/src/pages/tenant/reports/list-csp-licenses/index.jsx +++ b/src/pages/tenant/reports/list-csp-licenses/index.jsx @@ -93,7 +93,7 @@ const Page = () => { simpleColumns={simpleColumns} cardButton={ <> - diff --git a/src/utils/get-cipp-formatting.js b/src/utils/get-cipp-formatting.js index 25b1cd58c7ec..48e57d972f1a 100644 --- a/src/utils/get-cipp-formatting.js +++ b/src/utils/get-cipp-formatting.js @@ -219,6 +219,24 @@ export const getCippFormatting = (data, cellName, type, canReceive) => { } } + if (cellName === "PostExecution") { + const values = data ? data?.split(",").map((item) => item.trim()) : []; + if (values.length > 0) { + return isText + ? data + : values.map((value, index) => ( + + )); + } + } + if (cellName === "ClientId" || cellName === "role") { return isText ? data : ; }