From ea4c887c09bc79d3cbcb559901e5feadb1f08ab7 Mon Sep 17 00:00:00 2001 From: rvdwegen Date: Sun, 21 Jul 2024 19:24:03 +0200 Subject: [PATCH 01/52] Add bulk options to device list --- src/views/endpoint/intune/Devices.jsx | 38 +++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/src/views/endpoint/intune/Devices.jsx b/src/views/endpoint/intune/Devices.jsx index 85111eab9967..01ab8dd14071 100644 --- a/src/views/endpoint/intune/Devices.jsx +++ b/src/views/endpoint/intune/Devices.jsx @@ -283,6 +283,44 @@ const DevicesList = () => { path: '/api/ListDevices', reportName: `${tenant?.defaultDomainName}-Device-List`, params: { TenantFilter: tenant?.defaultDomainName }, + tableProps: { + keyField: 'id', + selectableRows: true, + actionsList: [ + { + label: 'Sync Device', + modal: true, + modalUrl: `/api/ExecDeviceAction?TenantFilter=${tenant.defaultDomainName}&GUID=!id&Action=syncDevice`, + modalMessage: 'Are you sure you want to Sync these device(s)?', + }, + { + label: 'Reboot Device(s)', + modal: true, + modalUrl: `/api/ExecDeviceAction?TenantFilter=${tenant.defaultDomainName}&GUID=!id&Action=rebootNow`, + modalMessage: 'Are you sure you want to reboot these device(s)?', + }, + { + label: 'Update Windows Defender', + modal: true, + modalUrl: `/api/ExecDeviceAction?TenantFilter=${tenant.defaultDomainName}&GUID=!id&Action=windowsDefenderUpdateSignatures`, + modalMessage: + 'Are you sure you want to update the Windows Defender signatures for these device(s)?', + }, + { + label: 'Rotate Local Admin Password', + modal: true, + modalUrl: `/api/ExecDeviceAction?TenantFilter=${tenant.defaultDomainName}&GUID=!id&Action=RotateLocalAdminPassword`, + modalMessage: 'Are you sure you want to rotate the password for these devices(s)?', + }, + { + label: 'Rotate Bitlocker Keys', + modal: true, + modalUrl: `/api/ExecDeviceAction?TenantFilter=${tenant.defaultDomainName}&GUID=!id&Action=rotateBitLockerKeys`, + modalMessage: + 'Are you sure you want to rotate the Bitlocker Keys for these device(s)?', + }, + ], + }, }} /> ) From 68c6af9c818fbb1e6a9281dda0e44933048abd27 Mon Sep 17 00:00:00 2001 From: Esco Date: Mon, 22 Jul 2024 14:25:39 +0200 Subject: [PATCH 02/52] Risk report quickfix * Added NoPagination true * Added $top 500 --- .../identity/administration/RiskyUsers.jsx | 20 +++---------------- src/views/identity/reports/RiskDetections.jsx | 4 +++- 2 files changed, 6 insertions(+), 18 deletions(-) diff --git a/src/views/identity/administration/RiskyUsers.jsx b/src/views/identity/administration/RiskyUsers.jsx index 727ee8dd896d..d51a899bef85 100644 --- a/src/views/identity/administration/RiskyUsers.jsx +++ b/src/views/identity/administration/RiskyUsers.jsx @@ -77,15 +77,6 @@ const RiskyUsers = () => { modalMessage: 'Are you sure you want to dismiss this users risk?', icon: , }, - /* TODO Add action for Compromised - { - label: 'Confirm Compromised', - color: 'info', - modal: true, - modalUrl: `/api/ExecBECRemediate?TenantFilter=${tenant.defaultDomainName}&userid=${row.id}`, - modalMessage: 'Are you sure you want to confirm this user as compromised?', - icon: , - },*/ ]} placement="end" visible={ocVisible} @@ -186,10 +177,12 @@ const RiskyUsers = () => { path: `api/ListGraphRequest`, reportName: `${tenant?.defaultDomainName}-ListRiskyUsers`, params: { - TenantFilter: tenant?.defaultDomainName, + TenantFilter: tenant.defaultDomainName, Endpoint: `identityProtection/riskyUsers`, $count: true, $orderby: 'riskLastUpdatedDateTime desc', + NoPagination: true, + $top: 500, }, tableProps: { selectableRows: true, @@ -200,13 +193,6 @@ const RiskyUsers = () => { model: true, modalUrl: `/api/ExecDismissRiskyUser?TenantFilter=${tenant.defaultDomainName}&userid=!id&userDisplayName=!userDisplayName`, }, - /* TODO Add action for Compromised - { - label: 'Confirm Compromised', - color: 'danger', - model: true, - modalUrl: `/api/ExecBECRemediate?TenantFilter=${tenant.defaultDomainName}&userid=!id`, - },*/ ], }, }} diff --git a/src/views/identity/reports/RiskDetections.jsx b/src/views/identity/reports/RiskDetections.jsx index b139fa058161..b031a9d9fc6b 100644 --- a/src/views/identity/reports/RiskDetections.jsx +++ b/src/views/identity/reports/RiskDetections.jsx @@ -214,10 +214,12 @@ const RiskDetections = () => { path: `api/ListGraphRequest`, reportName: `${tenant?.defaultDomainName}-RiskDetections-Report`, params: { - TenantFilter: tenant?.defaultDomainName, + TenantFilter: tenant.defaultDomainName, Endpoint: `identityProtection/riskDetections`, $count: true, $orderby: 'detectedDateTime desc', + NoPagination: true, + $top: 500, }, }} /> From eb418bf4782820798eaf562fe78470feaf4b49b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Kj=C3=A6rg=C3=A5rd?= Date: Tue, 23 Jul 2024 00:32:38 +0200 Subject: [PATCH 03/52] Added missing docs property --- src/data/standards.json | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/data/standards.json b/src/data/standards.json index c018b25b8a8e..dbcdd1f9a6c9 100644 --- a/src/data/standards.json +++ b/src/data/standards.json @@ -5,11 +5,6 @@ "tag": ["lowimpact"], "helpText": "Defines the email address to receive general updates and information related to M365 subscriptions. Leave a contact field blank if you do not want to update the contact information.", "docsDescription": "", - "disabledFeatures": { - "report": false, - "warn": false, - "remediate": false - }, "addedComponent": [ { "type": "input", @@ -645,7 +640,9 @@ "addedComponent": [], "label": "Disables Voice call as an MFA method", "impact": "High Impact", - "impactColour": "danger" + "impactColour": "danger", + "powershellEquivalent": "Update-MgBetaPolicyAuthenticationMethodPolicyAuthenticationMethodConfiguration", + "recommendedBy": [] }, { "name": "standards.DisableEmail", From d4e99bac477558e7ffe557e587f5258660b6847e Mon Sep 17 00:00:00 2001 From: John Duprey Date: Tue, 23 Jul 2024 16:28:44 -0400 Subject: [PATCH 04/52] Change placeholder --- src/views/cipp/Setup.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/cipp/Setup.jsx b/src/views/cipp/Setup.jsx index bf09e8b04baf..7f18e7c6a661 100644 --- a/src/views/cipp/Setup.jsx +++ b/src/views/cipp/Setup.jsx @@ -286,7 +286,7 @@ const Setup = () => { type="text" name="TenantID" label="Tenant ID" - placeholder="Enter the Tenant ID. e.g. mymsp.onmicrosoft.com. Leave blank to retain a previous key if this exists." + placeholder="Enter the Tenant ID. e.g. 1111-1111-1111-1111-11111. Leave blank to retain a previous key if this exists." /> From d0f3dcde9c62b69896f121719d00b20c772f315c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Kj=C3=A6rg=C3=A5rd?= Date: Wed, 24 Jul 2024 18:59:50 +0200 Subject: [PATCH 05/52] New standard for setting SharePoint sync button state --- src/data/standards.json | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/data/standards.json b/src/data/standards.json index dbcdd1f9a6c9..684111375dc8 100644 --- a/src/data/standards.json +++ b/src/data/standards.json @@ -2091,6 +2091,34 @@ "powershellEquivalent": "Graph API or Portal", "recommendedBy": [] }, + { + "name": "standards.SPSyncButtonState", + "cat": "SharePoint Standards", + "tag": ["mediumimpact"], + "helpText": "If disabled users in the tenant will no longer be able to use the Sync button to sync SharePoint content. However, existing synced content will remain functional on the user's computer.", + "addedComponent": [ + { + "type": "Select", + "label": "SharePoint Sync Button state", + "name": "standards.SPSyncButtonState.state", + "values": [ + { + "label": "Disabled", + "value": "true" + }, + { + "label": "Enabled", + "value": "false" + } + ] + } + ], + "label": "Set SharePoint sync button state", + "impact": "Medium Impact", + "impactColour": "warning", + "powershellEquivalent": "Set-SPOTenant -HideSyncButtonOnTeamSite:$true or $false", + "recommendedBy": [] + }, { "name": "standards.DisableSharePointLegacyAuth", "cat": "SharePoint Standards", From 9cff0cc548f27fdcbc38285e8cb3ead5aa6c2e56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Kj=C3=A6rg=C3=A5rd?= Date: Wed, 24 Jul 2024 21:09:17 +0200 Subject: [PATCH 06/52] Update standard with enable and disable options --- src/data/standards.json | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/src/data/standards.json b/src/data/standards.json index 684111375dc8..fefdf431455b 100644 --- a/src/data/standards.json +++ b/src/data/standards.json @@ -2078,24 +2078,35 @@ "name": "standards.DisableAddShortcutsToOneDrive", "cat": "SharePoint Standards", "tag": ["mediumimpact"], - "helpText": "When the feature is disabled the option Add shortcut to OneDrive will be removed. Any folders that have already been added will remain on the user's computer.", - "disabledFeatures": { - "report": true, - "warn": true, - "remediate": false - }, - "addedComponent": [], - "label": "Disable Add Shortcuts To OneDrive", + "helpText": "If disabled, the button Add shortcut to OneDrive will be removed and users in the tenant will no longer be able to add new shortcuts to their OneDrive. Existing shortcuts will remain functional", + "addedComponent": [ + { + "type": "Select", + "label": "Add Shortcuts To OneDrive button state", + "name": "standards.DisableAddShortcutsToOneDrive.state", + "values": [ + { + "label": "Disabled", + "value": "true" + }, + { + "label": "Enabled", + "value": "false" + } + ] + } + ], + "label": "Set Add Shortcuts To OneDrive button state", "impact": "Medium Impact", "impactColour": "warning", - "powershellEquivalent": "Graph API or Portal", + "powershellEquivalent": "Set-SPOTenant -DisableAddShortcutsToOneDrive $true or $false", "recommendedBy": [] }, { "name": "standards.SPSyncButtonState", "cat": "SharePoint Standards", "tag": ["mediumimpact"], - "helpText": "If disabled users in the tenant will no longer be able to use the Sync button to sync SharePoint content. However, existing synced content will remain functional on the user's computer.", + "helpText": "If disabled, users in the tenant will no longer be able to use the Sync button to sync SharePoint content on all sites. However, existing synced content will remain functional on the user's computer.", "addedComponent": [ { "type": "Select", @@ -2116,7 +2127,7 @@ "label": "Set SharePoint sync button state", "impact": "Medium Impact", "impactColour": "warning", - "powershellEquivalent": "Set-SPOTenant -HideSyncButtonOnTeamSite:$true or $false", + "powershellEquivalent": "Set-SPOTenant -HideSyncButtonOnTeamSite $true or $false", "recommendedBy": [] }, { From 4b12ee52753405687e448b64d11bc66cfe55da23 Mon Sep 17 00:00:00 2001 From: Esco Date: Fri, 19 Jul 2024 13:26:53 +0200 Subject: [PATCH 07/52] Added List Malware Filters --- src/_nav.jsx | 5 + src/importsMap.jsx | 1 + src/routes.json | 6 + .../email-exchange/reports/MalwareFilters.jsx | 207 ++++++++++++++++++ 4 files changed, 219 insertions(+) create mode 100644 src/views/email-exchange/reports/MalwareFilters.jsx diff --git a/src/_nav.jsx b/src/_nav.jsx index a3910a2f0018..aefc705355c0 100644 --- a/src/_nav.jsx +++ b/src/_nav.jsx @@ -793,6 +793,11 @@ const _nav = [ name: 'Phishing Policies', to: '/email/reports/phishing-policies', }, + { + component: CNavItem, + name: 'Malware Filters', + to: '/email/reports/malware-filters', + }, { component: CNavItem, name: 'Shared Mailbox with Enabled Account', diff --git a/src/importsMap.jsx b/src/importsMap.jsx index a8435a662dbd..6af8bac51461 100644 --- a/src/importsMap.jsx +++ b/src/importsMap.jsx @@ -134,6 +134,7 @@ import React from 'react' "/email/reports/message-trace": React.lazy(() => import('./views/email-exchange/reports/MessageTrace')), "/cipp/user-settings": React.lazy(() => import('./views/cipp/UserSettings')), "/email/reports/phishing-policies": React.lazy(() => import('./views/email-exchange/reports/PhishingPoliciesList')), + "/email/reports/malware-filters": React.lazy(() => import('./views/email-exchange/reports/MalwareFilters')), "/security/incidents/list-alerts": React.lazy(() => import('./views/security/incidents/ListAlerts')), "/security/incidents/list-incidents": React.lazy(() => import('./views/security/incidents/ListIncidents')), "/security/reports/list-device-compliance": React.lazy(() => import('./views/security/reports/ListDeviceComplianceReport')), diff --git a/src/routes.json b/src/routes.json index 9817ebc8aabd..5ae6152f0bd7 100644 --- a/src/routes.json +++ b/src/routes.json @@ -895,6 +895,12 @@ "component": "views/email-exchange/reports/PhishingPoliciesList", "allowedRoles": ["admin", "editor", "readonly"] }, + { + "path": "/email/reports/malware-filters", + "name": "Malware Filters", + "component": "views/email-exchange/reports/MalwareFilters", + "allowedRoles": ["admin", "editor", "readonly"] + }, { "name": "Security & Compliance", "path": "/security", diff --git a/src/views/email-exchange/reports/MalwareFilters.jsx b/src/views/email-exchange/reports/MalwareFilters.jsx new file mode 100644 index 000000000000..e77053c5be99 --- /dev/null +++ b/src/views/email-exchange/reports/MalwareFilters.jsx @@ -0,0 +1,207 @@ +import { useSelector } from 'react-redux' +import { CippPageList } from 'src/components/layout' +import { cellDateFormatter, cellBooleanFormatter, CellTip } from 'src/components/tables' +import { cellTableFormatter } from 'src/components/tables/CellTable' + +const ListMalwareFilters = () => { + const tenant = useSelector((state) => state.app.currentTenant) + + const Offcanvas = (row, rowIndex, formatExtraData) => { + const [ocVisible, setOCVisible] = useState(false) + + return ( + <> + setOCVisible(true)}> + + + , + modal: true, + modalUrl: `/api/EditMalwareFilter?State=Enable&TenantFilter=${tenant.defaultDomainName}&RuleName=${row.RuleName}`, + modalMessage: 'Are you sure you want to enable this rule?', + }, + { + label: 'Disable Rule', + color: 'info', + icon: , + modal: true, + modalUrl: `/api/EditMalwareFilter?State=Disable&TenantFilter=${tenant.defaultDomainName}&RuleName=${row.RuleName}`, + modalMessage: 'Are you sure you want to disable this rule?', + }, + /*{ + label: 'Delete Rule', + color: 'danger', + modal: true, + icon: , + modalUrl: `/api/RemoveMalwareFilter?TenantFilter=${tenant.defaultDomainName}&RuleName=${row.RuleName}`, + modalMessage: 'Are you sure you want to delete this rule?', + },*/ + ]} + placement="end" + visible={ocVisible} + id={row.id} + hideFunction={() => setOCVisible(false)} + /> + + ) + } + + const columns = [ + { + name: 'Rule Name', + selector: (row) => row['RuleName'], + sortable: true, + exportSelector: 'RuleName', + }, + { + name: 'Policy Name', + selector: (row) => row['Name'], + sortable: true, + exportSelector: 'Name', + }, + { + name: 'Enabled', + selector: (row) => row['State'], + exportSelector: 'State', + cell: cellBooleanFormatter(), + maxWidth: '40px', + }, + { + name: 'Priority', + selector: (row) => row['Priority'], + sortable: true, + exportSelector: 'Priority', + maxWidth: '40px', + }, + { + name: 'Recipient Domains', + selector: (row) => row['RecipientDomainIs'], + sortable: true, + exportSelector: 'RecipientDomainIs', + cell: cellTableFormatter('RecipientDomainIs'), + }, + { + name: 'File Filter', + selector: (row) => row['EnableFileFilter'], + exportSelector: 'EnableFileFilter', + cell: cellBooleanFormatter(), + maxWidth: '40px', + }, + { + name: 'File Types', + selector: (row) => row['FileTypes'], + sortable: true, + exportSelector: 'FileTypes', + cell: cellTableFormatter('FileTypes'), + }, + { + name: 'File Type Action', + selector: (row) => row['FileTypeAction'], + sortable: true, + exportSelector: 'FileTypeAction', + maxWidth: '200px', + }, + { + name: 'Zap', + selector: (row) => row['ZapEnabled'], + exportSelector: 'ZapEnabled', + cell: cellBooleanFormatter(), + maxWidth: '40px', + }, + { + name: 'Quarantine Tag', + selector: (row) => row['QuarantineTag'], + sortable: true, + exportSelector: 'QuarantineTag', + maxWidth: '200px', + }, + { + name: 'Internal Admin Notifications', + selector: (row) => row['EnableInternalSenderAdminNotifications'], + exportSelector: 'EnableInternalSenderAdminNotifications', + cell: cellBooleanFormatter(), + maxWidth: '40px', + }, + { + name: 'Internal Sender Admin Address', + selector: (row) => row['InternalSenderAdminAddress'], + sortable: true, + exportSelector: 'InternalSenderAdminAddress', + }, + { + name: 'External Admin Notifications', + selector: (row) => row['EnableExternalSenderAdminNotifications'], + exportSelector: 'EnableExternalSenderAdminNotifications', + cell: cellBooleanFormatter(), + maxWidth: '40px', + }, + { + name: 'External Sender Admin Address', + selector: (row) => row['ExternalSenderAdminAddress'], + sortable: true, + exportSelector: 'ExternalSenderAdminAddress', + }, + { + name: 'Creation Date', + selector: (row) => row['WhenCreated'], + sortable: true, + exportSelector: 'WhenCreated', + cell: cellDateFormatter(), + maxWidth: '150px', + }, + { + name: 'Last Modified Date', + selector: (row) => row['WhenChanged'], + sortable: true, + exportSelector: 'WhenChanged', + cell: cellDateFormatter(), + maxWidth: '150px', + }, + ] + + return ( + <> + + + ) +} + +export default ListMalwareFilters From 9b4206e32e6ddd6ae69388c2d7fc2bb0c3cdda75 Mon Sep 17 00:00:00 2001 From: Esco Date: Wed, 24 Jul 2024 14:54:16 +0200 Subject: [PATCH 08/52] Added List SafeLinks Filters --- src/_nav.jsx | 5 + src/importsMap.jsx | 1 + src/routes.json | 6 + .../reports/SafeLinksFilters.jsx | 216 ++++++++++++++++++ 4 files changed, 228 insertions(+) create mode 100644 src/views/email-exchange/reports/SafeLinksFilters.jsx diff --git a/src/_nav.jsx b/src/_nav.jsx index aefc705355c0..5b6e8cb0e6c5 100644 --- a/src/_nav.jsx +++ b/src/_nav.jsx @@ -798,6 +798,11 @@ const _nav = [ name: 'Malware Filters', to: '/email/reports/malware-filters', }, + { + component: CNavItem, + name: 'Safe Links Filters', + to: '/email/reports/safelinks-filters', + }, { component: CNavItem, name: 'Shared Mailbox with Enabled Account', diff --git a/src/importsMap.jsx b/src/importsMap.jsx index 6af8bac51461..3990a1551623 100644 --- a/src/importsMap.jsx +++ b/src/importsMap.jsx @@ -135,6 +135,7 @@ import React from 'react' "/cipp/user-settings": React.lazy(() => import('./views/cipp/UserSettings')), "/email/reports/phishing-policies": React.lazy(() => import('./views/email-exchange/reports/PhishingPoliciesList')), "/email/reports/malware-filters": React.lazy(() => import('./views/email-exchange/reports/MalwareFilters')), + "/email/reports/safelinks-filters": React.lazy(() => import('./views/email-exchange/reports/SafeLinksFilters')), "/security/incidents/list-alerts": React.lazy(() => import('./views/security/incidents/ListAlerts')), "/security/incidents/list-incidents": React.lazy(() => import('./views/security/incidents/ListIncidents')), "/security/reports/list-device-compliance": React.lazy(() => import('./views/security/reports/ListDeviceComplianceReport')), diff --git a/src/routes.json b/src/routes.json index 5ae6152f0bd7..16cfe3e8268c 100644 --- a/src/routes.json +++ b/src/routes.json @@ -901,6 +901,12 @@ "component": "views/email-exchange/reports/MalwareFilters", "allowedRoles": ["admin", "editor", "readonly"] }, + { + "path": "/email/reports/safelinks-filters", + "name": "Safe Links Filters", + "component": "views/email-exchange/reports/SafeLinksFilters", + "allowedRoles": ["admin", "editor", "readonly"] + }, { "name": "Security & Compliance", "path": "/security", diff --git a/src/views/email-exchange/reports/SafeLinksFilters.jsx b/src/views/email-exchange/reports/SafeLinksFilters.jsx new file mode 100644 index 000000000000..122526e52352 --- /dev/null +++ b/src/views/email-exchange/reports/SafeLinksFilters.jsx @@ -0,0 +1,216 @@ +import { useSelector } from 'react-redux' +import { CippPageList } from 'src/components/layout' +import { cellDateFormatter, cellBooleanFormatter, CellTip } from 'src/components/tables' +import { cellTableFormatter } from 'src/components/tables/CellTable' + +const ListSafeLinksFilters = () => { + const tenant = useSelector((state) => state.app.currentTenant) + + const Offcanvas = (row, rowIndex, formatExtraData) => { + const [ocVisible, setOCVisible] = useState(false) + + return ( + <> + setOCVisible(true)}> + + + , + modal: true, + modalUrl: `/api/EditSafeLinksFilter?State=Enable&TenantFilter=${tenant.defaultDomainName}&RuleName=${row.RuleName}`, + modalMessage: 'Are you sure you want to enable this rule?', + }, + { + label: 'Disable Rule', + color: 'info', + icon: , + modal: true, + modalUrl: `/api/EditSafeLinksFilter?State=Disable&TenantFilter=${tenant.defaultDomainName}&RuleName=${row.RuleName}`, + modalMessage: 'Are you sure you want to disable this rule?', + }, + /*{ + label: 'Delete Rule', + color: 'danger', + modal: true, + icon: , + modalUrl: `/api/RemoveSafeLinksFilter?TenantFilter=${tenant.defaultDomainName}&RuleName=${row.RuleName}`, + modalMessage: 'Are you sure you want to delete this rule?', + },*/ + ]} + placement="end" + visible={ocVisible} + id={row.id} + hideFunction={() => setOCVisible(false)} + /> + + ) + } + + const columns = [ + { + name: 'Rule Name', + selector: (row) => row['RuleName'], + sortable: true, + exportSelector: 'RuleName', + }, + { + name: 'Policy Name', + selector: (row) => row['Name'], + sortable: true, + exportSelector: 'Name', + }, + { + name: 'Enabled', + selector: (row) => row['State'], + exportSelector: 'State', + cell: cellBooleanFormatter(), + maxWidth: '40px', + }, + { + name: 'Priority', + selector: (row) => row['Priority'], + sortable: true, + exportSelector: 'Priority', + maxWidth: '40px', + }, + { + name: 'Recipient Domains', + selector: (row) => row['RecipientDomainIs'], + sortable: true, + exportSelector: 'RecipientDomainIs', + cell: cellTableFormatter('RecipientDomainIs'), + }, + { + name: 'SafeLinks For Email', + selector: (row) => row['EnableSafeLinksForEmail'], + exportSelector: 'EnableSafeLinksForEmail', + cell: cellBooleanFormatter(), + maxWidth: '40px', + }, + { + name: 'SafeLinks For Teams', + selector: (row) => row['EnableSafeLinksForTeams'], + exportSelector: 'EnableSafeLinksForTeams', + cell: cellBooleanFormatter(), + maxWidth: '40px', + }, + { + name: 'SafeLinks For Office', + selector: (row) => row['EnableSafeLinksForOffice'], + exportSelector: 'EnableSafeLinksForOffice', + cell: cellBooleanFormatter(), + maxWidth: '40px', + }, + { + name: 'Track Clicks', + selector: (row) => row['TrackClicks'], + exportSelector: 'TrackClicks', + cell: cellBooleanFormatter(), + maxWidth: '40px', + }, + { + name: 'Scan Urls', + selector: (row) => row['ScanUrls'], + exportSelector: 'ScanUrls', + cell: cellBooleanFormatter(), + maxWidth: '40px', + }, + { + name: 'Enable For Internal Senders', + selector: (row) => row['EnableForInternalSenders'], + exportSelector: 'EnableForInternalSenders', + cell: cellBooleanFormatter(), + maxWidth: '40px', + }, + { + name: 'Deliver Message After Scan', + selector: (row) => row['DeliverMessageAfterScan'], + exportSelector: 'DeliverMessageAfterScan', + cell: cellBooleanFormatter(), + maxWidth: '40px', + }, + { + name: 'Allow Click Through', + selector: (row) => row['AllowClickThrough'], + exportSelector: 'AllowClickThrough', + cell: cellBooleanFormatter(), + maxWidth: '40px', + }, + { + name: 'Disable Url Rewrite', + selector: (row) => row['DisableUrlRewrite'], + exportSelector: 'DisableUrlRewrite', + cell: cellBooleanFormatter(), + maxWidth: '40px', + }, + { + name: 'Organization Branding', + selector: (row) => row['EnableOrganizationBranding'], + exportSelector: 'EnableOrganizationBranding', + cell: cellBooleanFormatter(), + maxWidth: '40px', + }, + { + name: 'Creation Date', + selector: (row) => row['WhenCreated'], + sortable: true, + exportSelector: 'WhenCreated', + cell: cellDateFormatter(), + maxWidth: '150px', + }, + { + name: 'Last Modified Date', + selector: (row) => row['WhenChanged'], + sortable: true, + exportSelector: 'WhenChanged', + cell: cellDateFormatter(), + maxWidth: '150px', + }, + ] + + return ( + <> + + + ) +} + +export default ListSafeLinksFilters From 516cc8cb9ce2ad5de103900d4d1269d8ca4f3f2d Mon Sep 17 00:00:00 2001 From: Esco Date: Thu, 25 Jul 2024 09:25:29 +0200 Subject: [PATCH 09/52] Added List SafeAttachment Filters --- src/_nav.jsx | 5 + src/importsMap.jsx | 1 + src/routes.json | 6 + .../reports/SafeAttachmentsFilters.jsx | 171 ++++++++++++++++++ 4 files changed, 183 insertions(+) create mode 100644 src/views/email-exchange/reports/SafeAttachmentsFilters.jsx diff --git a/src/_nav.jsx b/src/_nav.jsx index 5b6e8cb0e6c5..08a5f65d3e7f 100644 --- a/src/_nav.jsx +++ b/src/_nav.jsx @@ -803,6 +803,11 @@ const _nav = [ name: 'Safe Links Filters', to: '/email/reports/safelinks-filters', }, + { + component: CNavItem, + name: 'Safe Attachments Filters', + to: '/email/reports/safeattachments-filters', + }, { component: CNavItem, name: 'Shared Mailbox with Enabled Account', diff --git a/src/importsMap.jsx b/src/importsMap.jsx index 3990a1551623..af1f13be9479 100644 --- a/src/importsMap.jsx +++ b/src/importsMap.jsx @@ -136,6 +136,7 @@ import React from 'react' "/email/reports/phishing-policies": React.lazy(() => import('./views/email-exchange/reports/PhishingPoliciesList')), "/email/reports/malware-filters": React.lazy(() => import('./views/email-exchange/reports/MalwareFilters')), "/email/reports/safelinks-filters": React.lazy(() => import('./views/email-exchange/reports/SafeLinksFilters')), + "/email/reports/safeattachments-filters": React.lazy(() => import('./views/email-exchange/reports/SafeAttachmentsFilters')), "/security/incidents/list-alerts": React.lazy(() => import('./views/security/incidents/ListAlerts')), "/security/incidents/list-incidents": React.lazy(() => import('./views/security/incidents/ListIncidents')), "/security/reports/list-device-compliance": React.lazy(() => import('./views/security/reports/ListDeviceComplianceReport')), diff --git a/src/routes.json b/src/routes.json index 16cfe3e8268c..c814a8bff979 100644 --- a/src/routes.json +++ b/src/routes.json @@ -907,6 +907,12 @@ "component": "views/email-exchange/reports/SafeLinksFilters", "allowedRoles": ["admin", "editor", "readonly"] }, + { + "path": "/email/reports/safeattachments-filters", + "name": "Safe Attachment Filters", + "component": "views/email-exchange/reports/SafeAttachmentsFilters", + "allowedRoles": ["admin", "editor", "readonly"] + }, { "name": "Security & Compliance", "path": "/security", diff --git a/src/views/email-exchange/reports/SafeAttachmentsFilters.jsx b/src/views/email-exchange/reports/SafeAttachmentsFilters.jsx new file mode 100644 index 000000000000..43248aefdf32 --- /dev/null +++ b/src/views/email-exchange/reports/SafeAttachmentsFilters.jsx @@ -0,0 +1,171 @@ +import { useSelector } from 'react-redux' +import { CippPageList } from 'src/components/layout' +import { cellDateFormatter, cellBooleanFormatter, CellTip } from 'src/components/tables' +import { cellTableFormatter } from 'src/components/tables/CellTable' + +const ListSafeAttachmentsFilters = () => { + const tenant = useSelector((state) => state.app.currentTenant) + + const Offcanvas = (row, rowIndex, formatExtraData) => { + const [ocVisible, setOCVisible] = useState(false) + + return ( + <> + setOCVisible(true)}> + + + , + modal: true, + modalUrl: `/api/EditSafeAttachmentsFilter?State=Enable&TenantFilter=${tenant.defaultDomainName}&RuleName=${row.RuleName}`, + modalMessage: 'Are you sure you want to enable this rule?', + }, + { + label: 'Disable Rule', + color: 'info', + icon: , + modal: true, + modalUrl: `/api/EditSafeAttachmentsFilter?State=Disable&TenantFilter=${tenant.defaultDomainName}&RuleName=${row.RuleName}`, + modalMessage: 'Are you sure you want to disable this rule?', + }, + /*{ + label: 'Delete Rule', + color: 'danger', + modal: true, + icon: , + modalUrl: `/api/RemoveSafeAttachmentsFilter?TenantFilter=${tenant.defaultDomainName}&RuleName=${row.RuleName}`, + modalMessage: 'Are you sure you want to delete this rule?', + },*/ + ]} + placement="end" + visible={ocVisible} + id={row.id} + hideFunction={() => setOCVisible(false)} + /> + + ) + } + + const columns = [ + { + name: 'Rule Name', + selector: (row) => row['RuleName'], + sortable: true, + exportSelector: 'RuleName', + }, + { + name: 'Policy Name', + selector: (row) => row['Name'], + sortable: true, + exportSelector: 'Name', + }, + { + name: 'Enabled', + selector: (row) => row['State'], + exportSelector: 'State', + cell: cellBooleanFormatter(), + maxWidth: '40px', + }, + { + name: 'Priority', + selector: (row) => row['Priority'], + sortable: true, + exportSelector: 'Priority', + maxWidth: '40px', + }, + { + name: 'Recipient Domains', + selector: (row) => row['RecipientDomainIs'], + sortable: true, + exportSelector: 'RecipientDomainIs', + cell: cellTableFormatter('RecipientDomainIs'), + }, + { + name: 'Action', + selector: (row) => row['Action'], + sortable: true, + exportSelector: 'Action', + }, + { + name: 'QuarantineTag', + selector: (row) => row['QuarantineTag'], + sortable: true, + exportSelector: 'QuarantineTag', + }, + { + name: 'Redirect', + selector: (row) => row['Redirect'], + exportSelector: 'Redirect', + cell: cellBooleanFormatter(), + maxWidth: '40px', + }, + { + name: 'Redirect Address', + selector: (row) => row['RedirectAddress'], + sortable: true, + exportSelector: 'RedirectAddress', + }, + { + name: 'Creation Date', + selector: (row) => row['WhenCreated'], + sortable: true, + exportSelector: 'WhenCreated', + cell: cellDateFormatter(), + maxWidth: '150px', + }, + { + name: 'Last Modified Date', + selector: (row) => row['WhenChanged'], + sortable: true, + exportSelector: 'WhenChanged', + cell: cellDateFormatter(), + maxWidth: '150px', + }, + ] + + return ( + <> + + + ) +} + +export default ListSafeAttachmentsFilters From 5d0916931bbddcb80f23fcd34f140e285600725f Mon Sep 17 00:00:00 2001 From: Esco Date: Thu, 25 Jul 2024 10:08:57 +0200 Subject: [PATCH 10/52] Updated List AntiPhishing Filters --- src/_nav.jsx | 4 +- src/importsMap.jsx | 2 +- src/routes.json | 6 +- .../reports/AntiPhishingFilters.jsx | 283 ++++++++++++++++++ .../reports/PhishingPoliciesList.jsx | 73 ----- 5 files changed, 289 insertions(+), 79 deletions(-) create mode 100644 src/views/email-exchange/reports/AntiPhishingFilters.jsx delete mode 100644 src/views/email-exchange/reports/PhishingPoliciesList.jsx diff --git a/src/_nav.jsx b/src/_nav.jsx index 08a5f65d3e7f..c0faa91279c0 100644 --- a/src/_nav.jsx +++ b/src/_nav.jsx @@ -790,8 +790,8 @@ const _nav = [ }, { component: CNavItem, - name: 'Phishing Policies', - to: '/email/reports/phishing-policies', + name: 'Anti-Phishing Filters', + to: '/email/reports/antiphishing-filters', }, { component: CNavItem, diff --git a/src/importsMap.jsx b/src/importsMap.jsx index af1f13be9479..eaa260ad4dbd 100644 --- a/src/importsMap.jsx +++ b/src/importsMap.jsx @@ -133,7 +133,7 @@ import React from 'react' "/email/reports/mailbox-cas-settings": React.lazy(() => import('./views/email-exchange/reports/MailboxClientAccessSettingsList')), "/email/reports/message-trace": React.lazy(() => import('./views/email-exchange/reports/MessageTrace')), "/cipp/user-settings": React.lazy(() => import('./views/cipp/UserSettings')), - "/email/reports/phishing-policies": React.lazy(() => import('./views/email-exchange/reports/PhishingPoliciesList')), + "/email/reports/antiphishing-filters": React.lazy(() => import('./views/email-exchange/reports/AntiPhishingFilters')), "/email/reports/malware-filters": React.lazy(() => import('./views/email-exchange/reports/MalwareFilters')), "/email/reports/safelinks-filters": React.lazy(() => import('./views/email-exchange/reports/SafeLinksFilters')), "/email/reports/safeattachments-filters": React.lazy(() => import('./views/email-exchange/reports/SafeAttachmentsFilters')), diff --git a/src/routes.json b/src/routes.json index c814a8bff979..f5b6053359a7 100644 --- a/src/routes.json +++ b/src/routes.json @@ -890,9 +890,9 @@ "allowedRoles": ["admin", "editor", "readonly"] }, { - "name": "Phishing Policies", - "path": "/email/reports/phishing-policies", - "component": "views/email-exchange/reports/PhishingPoliciesList", + "path": "/email/reports/antiphishing-filters", + "name": "Anti Phishing Filters", + "component": "views/email-exchange/reports/AntiPhishingFilters", "allowedRoles": ["admin", "editor", "readonly"] }, { diff --git a/src/views/email-exchange/reports/AntiPhishingFilters.jsx b/src/views/email-exchange/reports/AntiPhishingFilters.jsx new file mode 100644 index 000000000000..b9be79b8d074 --- /dev/null +++ b/src/views/email-exchange/reports/AntiPhishingFilters.jsx @@ -0,0 +1,283 @@ +import { useSelector } from 'react-redux' +import { CippPageList } from 'src/components/layout' +import { cellDateFormatter, cellBooleanFormatter, CellTip } from 'src/components/tables' +import { cellTableFormatter } from 'src/components/tables/CellTable' + +const ListAntiPhishingFilters = () => { + const tenant = useSelector((state) => state.app.currentTenant) + + const Offcanvas = (row, rowIndex, formatExtraData) => { + const [ocVisible, setOCVisible] = useState(false) + + return ( + <> + setOCVisible(true)}> + + + , + modal: true, + modalUrl: `/api/EditAntiPhishingFilter?State=Enable&TenantFilter=${tenant.defaultDomainName}&RuleName=${row.RuleName}`, + modalMessage: 'Are you sure you want to enable this rule?', + }, + { + label: 'Disable Rule', + color: 'info', + icon: , + modal: true, + modalUrl: `/api/EditAntiPhishingFilter?State=Disable&TenantFilter=${tenant.defaultDomainName}&RuleName=${row.RuleName}`, + modalMessage: 'Are you sure you want to disable this rule?', + }, + /*{ + label: 'Delete Rule', + color: 'danger', + modal: true, + icon: , + modalUrl: `/api/RemoveAntiPhishingFilter?TenantFilter=${tenant.defaultDomainName}&RuleName=${row.RuleName}`, + modalMessage: 'Are you sure you want to delete this rule?', + },*/ + ]} + placement="end" + visible={ocVisible} + id={row.id} + hideFunction={() => setOCVisible(false)} + /> + + ) + } + + const columns = [ + { + name: 'Rule Name', + selector: (row) => row['RuleName'], + sortable: true, + exportSelector: 'RuleName', + }, + { + name: 'Policy Name', + selector: (row) => row['Name'], + sortable: true, + exportSelector: 'Name', + }, + { + name: 'Enabled', + selector: (row) => row['State'], + exportSelector: 'State', + cell: cellBooleanFormatter(), + maxWidth: '40px', + }, + { + name: 'Priority', + selector: (row) => row['Priority'], + sortable: true, + exportSelector: 'Priority', + maxWidth: '40px', + }, + { + name: 'Recipient Domains', + selector: (row) => row['RecipientDomainIs'], + sortable: true, + exportSelector: 'RecipientDomainIs', + cell: cellTableFormatter('RecipientDomainIs'), + }, + { + name: 'Excluded Domains', + selector: (row) => row['ExcludedDomains'], + sortable: true, + exportSelector: 'ExcludedDomains', + cell: cellTableFormatter('ExcludedDomains'), + }, + { + name: 'Excluded Senders', + selector: (row) => row['ExcludedSenders'], + sortable: true, + exportSelector: 'ExcludedSenders', + cell: cellTableFormatter('ExcludedSenders'), + }, + { + name: 'PhishThresholdLevel', + selector: (row) => row['PhishThresholdLevel'], + sortable: true, + exportSelector: 'PhishThresholdLevel', + }, + { + name: 'Mailbox Intelligence', + selector: (row) => row['EnableMailboxIntelligence'], + exportSelector: 'EnableMailboxIntelligence', + cell: cellBooleanFormatter(), + maxWidth: '40px', + }, + { + name: 'Mailbox Intelligence Protection', + selector: (row) => row['EnableMailboxIntelligenceProtection'], + exportSelector: 'EnableMailboxIntelligenceProtection', + cell: cellBooleanFormatter(), + maxWidth: '40px', + }, + { + name: 'Spoof Intelligence', + selector: (row) => row['EnableSpoofIntelligence'], + exportSelector: 'EnableSpoofIntelligence', + cell: cellBooleanFormatter(), + maxWidth: '40px', + }, + { + name: 'First Contact Safety Tips', + selector: (row) => row['EnableFirstContactSafetyTips'], + exportSelector: 'EnableFirstContactSafetyTips', + cell: cellBooleanFormatter(), + maxWidth: '40px', + }, + { + name: 'Similar Users Safety Tips', + selector: (row) => row['EnableSimilarUsersSafetyTips'], + exportSelector: 'EnableSimilarUsersSafetyTips', + cell: cellBooleanFormatter(), + maxWidth: '40px', + }, + { + name: 'Similar Domain Safety Tips', + selector: (row) => row['EnableSimilarDomainsSafetyTips'], + exportSelector: 'EnableSimilarDomainsSafetyTips', + cell: cellBooleanFormatter(), + maxWidth: '40px', + }, + { + name: 'Unusual Characters Safety Tips', + selector: (row) => row['EnableUnusualCharactersSafetyTips'], + exportSelector: 'EnableUnusualCharactersSafetyTips', + cell: cellBooleanFormatter(), + maxWidth: '40px', + }, + { + name: 'Unauthenticated Sender', + selector: (row) => row['EnableUnauthenticatedSender'], + exportSelector: 'EnableUnauthenticatedSender', + cell: cellBooleanFormatter(), + maxWidth: '40px', + }, + { + name: 'ViaTag', + selector: (row) => row['EnableViaTag'], + exportSelector: 'EnableViaTag', + cell: cellBooleanFormatter(), + maxWidth: '40px', + }, + { + name: 'Organization Domains Protection', + selector: (row) => row['EnableOrganizationDomainsProtection'], + exportSelector: 'EnableOrganizationDomainsProtection', + cell: cellBooleanFormatter(), + }, + { + name: 'Authentication Fail Action', + selector: (row) => row['AuthenticationFailAction'], + exportSelector: 'AuthenticationFailAction', + maxWidt: '100px', + }, + { + name: 'Spoof Quarantine Tag', + selector: (row) => row['SpoofQuarantineTag'], + exportSelector: 'SpoofQuarantineTag', + maxWidth: '100px', + }, + { + name: 'MailboxIntelligence Protection Action', + selector: (row) => row['MailboxIntelligenceProtectionAction'], + exportSelector: 'MailboxIntelligenceProtectionAction', + maxWidth: '100px', + }, + { + name: 'Mailbox Intelligence Quarantine Tag', + selector: (row) => row['MailboxIntelligenceQuarantineTag'], + exportSelector: 'MailboxIntelligenceQuarantineTag', + maxWidth: '100px', + }, + { + name: 'Targeted UserProtection Action', + selector: (row) => row['TargetedUserProtectionAction'], + exportSelector: 'TargetedUserProtectionAction', + maxWidth: '100px', + }, + { + name: 'Targeted UserQuarantine Tag', + selector: (row) => row['TargetedUserQuarantineTag'], + exportSelector: 'TargetedUserQuarantineTag', + maxWidth: '100px', + }, + { + name: 'Targeted Domain Protection Action', + selector: (row) => row['TargetedDomainProtectionAction'], + exportSelector: 'TargetedDomainProtectionAction', + maxWidth: '100px', + }, + { + name: 'Targeted Domain Quarantine Tag', + selector: (row) => row['TargetedDomainQuarantineTag'], + exportSelector: 'TargetedDomainQuarantineTag', + maxWidth: '100px', + }, + { + name: 'Creation Date', + selector: (row) => row['WhenCreated'], + sortable: true, + exportSelector: 'WhenCreated', + cell: cellDateFormatter(), + maxWidth: '150px', + }, + { + name: 'Last Modified Date', + selector: (row) => row['WhenChanged'], + sortable: true, + exportSelector: 'WhenChanged', + cell: cellDateFormatter(), + maxWidth: '150px', + }, + ] + + return ( + <> + + + ) +} + +export default ListAntiPhishingFilters diff --git a/src/views/email-exchange/reports/PhishingPoliciesList.jsx b/src/views/email-exchange/reports/PhishingPoliciesList.jsx deleted file mode 100644 index b4cd5ba23a01..000000000000 --- a/src/views/email-exchange/reports/PhishingPoliciesList.jsx +++ /dev/null @@ -1,73 +0,0 @@ -import React from 'react' -import { useSelector } from 'react-redux' -import { CippPageList } from 'src/components/layout/CippPage' -import { cellBooleanFormatter, cellDateFormatter } from 'src/components/tables' - -//TODO: Add CellBoolean -const columns = [ - { - selector: (row) => row['Name'], - name: 'Name', - sortable: true, - exportSelector: 'Name', - }, - { - selector: (row) => row['PhishThresholdLevel'], - name: 'Phish Threshold Level', - sortable: true, - exportSelector: 'PhishThresholdLevel', - }, - { - selector: (row) => row['Enabled'], - name: 'Enabled', - sortable: true, - cell: cellBooleanFormatter({ warning: true, colourless: true }), - exportSelector: 'Enabled', - }, - { - selector: (row) => row['ExcludedSenders'], - name: 'Excluded Senders', - sortable: true, - cell: cellBooleanFormatter({ warning: true, colourless: true, noDataIsFalse: true }), - exportSelector: 'ExcludedSenders', - }, - { - selector: (row) => row['ExcludedDomains'], - name: 'Excluded Domains', - sortable: true, - cell: cellBooleanFormatter(), - exportSelector: 'ExcludedDomains', - }, - { - selector: (row) => row['WhenChangedUTC'], - name: 'Last Change Date (Local)', - sortable: true, - cell: cellDateFormatter(), - exportSelector: 'WhenChangedUTC', - }, - { - selector: (row) => row['Priority'], - name: 'Priority', - sortable: true, - exportSelector: 'Priority', - }, -] - -const MailboxList = () => { - const tenant = useSelector((state) => state.app.currentTenant) - - return ( - - ) -} - -export default MailboxList From d4e642c315c39959391367dffc7a71404a627386 Mon Sep 17 00:00:00 2001 From: Esco Date: Thu, 25 Jul 2024 11:17:38 +0200 Subject: [PATCH 11/52] Added Edit Safe AttachmentsFilter --- .../email-exchange/reports/SafeAttachmentsFilters.jsx | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/views/email-exchange/reports/SafeAttachmentsFilters.jsx b/src/views/email-exchange/reports/SafeAttachmentsFilters.jsx index 43248aefdf32..a9257edfd7e4 100644 --- a/src/views/email-exchange/reports/SafeAttachmentsFilters.jsx +++ b/src/views/email-exchange/reports/SafeAttachmentsFilters.jsx @@ -1,3 +1,8 @@ +import { CButton } from '@coreui/react' +import { faBan, faBook, faCheck, faEllipsisV, faTrash } from '@fortawesome/free-solid-svg-icons' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import React, { useState } from 'react' +import { CippActionsOffcanvas } from 'src/components/utilities' import { useSelector } from 'react-redux' import { CippPageList } from 'src/components/layout' import { cellDateFormatter, cellBooleanFormatter, CellTip } from 'src/components/tables' @@ -148,6 +153,11 @@ const ListSafeAttachmentsFilters = () => { cell: cellDateFormatter(), maxWidth: '150px', }, + { + name: 'Actions', + cell: Offcanvas, + maxWidth: '80px', + }, ] return ( From 6f70b2cf8f0617f17cccbed612d86f71ceabe5c1 Mon Sep 17 00:00:00 2001 From: Esco Date: Thu, 25 Jul 2024 12:30:32 +0200 Subject: [PATCH 12/52] Added Edit Anti-Phishing Filter --- .../email-exchange/reports/AntiPhishingFilters.jsx | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/views/email-exchange/reports/AntiPhishingFilters.jsx b/src/views/email-exchange/reports/AntiPhishingFilters.jsx index b9be79b8d074..ed68929c2005 100644 --- a/src/views/email-exchange/reports/AntiPhishingFilters.jsx +++ b/src/views/email-exchange/reports/AntiPhishingFilters.jsx @@ -1,3 +1,8 @@ +import { CButton } from '@coreui/react' +import { faBan, faBook, faCheck, faEllipsisV, faTrash } from '@fortawesome/free-solid-svg-icons' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import React, { useState } from 'react' +import { CippActionsOffcanvas } from 'src/components/utilities' import { useSelector } from 'react-redux' import { CippPageList } from 'src/components/layout' import { cellDateFormatter, cellBooleanFormatter, CellTip } from 'src/components/tables' @@ -260,6 +265,11 @@ const ListAntiPhishingFilters = () => { cell: cellDateFormatter(), maxWidth: '150px', }, + { + name: 'Actions', + cell: Offcanvas, + maxWidth: '80px', + }, ] return ( From b4aa3dddc4b8c8a5cc94622e4d77540c0c13bb5c Mon Sep 17 00:00:00 2001 From: Esco Date: Thu, 25 Jul 2024 12:30:44 +0200 Subject: [PATCH 13/52] Added Edit Malware Filter Update SafeLinksFilters.jsx --- src/views/email-exchange/reports/MalwareFilters.jsx | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/views/email-exchange/reports/MalwareFilters.jsx b/src/views/email-exchange/reports/MalwareFilters.jsx index e77053c5be99..ebd9d5ad354c 100644 --- a/src/views/email-exchange/reports/MalwareFilters.jsx +++ b/src/views/email-exchange/reports/MalwareFilters.jsx @@ -1,3 +1,8 @@ +import { CButton } from '@coreui/react' +import { faBan, faBook, faCheck, faEllipsisV, faTrash } from '@fortawesome/free-solid-svg-icons' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import React, { useState } from 'react' +import { CippActionsOffcanvas } from 'src/components/utilities' import { useSelector } from 'react-redux' import { CippPageList } from 'src/components/layout' import { cellDateFormatter, cellBooleanFormatter, CellTip } from 'src/components/tables' @@ -184,6 +189,11 @@ const ListMalwareFilters = () => { cell: cellDateFormatter(), maxWidth: '150px', }, + { + name: 'Actions', + cell: Offcanvas, + maxWidth: '80px', + }, ] return ( From 9213b34c7fd92c865af801973c092527ecd1f57d Mon Sep 17 00:00:00 2001 From: Esco Date: Thu, 25 Jul 2024 12:30:54 +0200 Subject: [PATCH 14/52] Added Edit SafeLinks Filter --- src/views/email-exchange/reports/SafeLinksFilters.jsx | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/views/email-exchange/reports/SafeLinksFilters.jsx b/src/views/email-exchange/reports/SafeLinksFilters.jsx index 122526e52352..68c9d07305ff 100644 --- a/src/views/email-exchange/reports/SafeLinksFilters.jsx +++ b/src/views/email-exchange/reports/SafeLinksFilters.jsx @@ -1,3 +1,8 @@ +import { CButton } from '@coreui/react' +import { faBan, faBook, faCheck, faEllipsisV, faTrash } from '@fortawesome/free-solid-svg-icons' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import React, { useState } from 'react' +import { CippActionsOffcanvas } from 'src/components/utilities' import { useSelector } from 'react-redux' import { CippPageList } from 'src/components/layout' import { cellDateFormatter, cellBooleanFormatter, CellTip } from 'src/components/tables' @@ -193,6 +198,11 @@ const ListSafeLinksFilters = () => { cell: cellDateFormatter(), maxWidth: '150px', }, + { + name: 'Actions', + cell: Offcanvas, + maxWidth: '80px', + }, ] return ( From 0d4dd6f3fd24d2a8d701bd27a0b1601346d6c500 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 25 Jul 2024 17:57:55 -0400 Subject: [PATCH 15/52] MFA report update --- src/views/identity/reports/MFAReport.jsx | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/views/identity/reports/MFAReport.jsx b/src/views/identity/reports/MFAReport.jsx index 27f09aecfa3b..7eb6859a3b07 100644 --- a/src/views/identity/reports/MFAReport.jsx +++ b/src/views/identity/reports/MFAReport.jsx @@ -3,6 +3,7 @@ import { useSelector } from 'react-redux' import { cellBooleanFormatter, CellTip } from 'src/components/tables' import { CippPageList } from 'src/components/layout' import { Row } from 'react-bootstrap' +import { cellGenericFormatter } from 'src/components/tables/CellGenericFormat' const columns = [ { @@ -57,9 +58,23 @@ const columns = [ selector: (row) => row['CoveredByCA'], name: 'Enforced via Conditional Access', sortable: true, - cell: (row) => CellTip(row['CoveredByCA']), + cell: cellGenericFormatter(), exportSelector: 'CoveredByCA', }, + { + selector: (row) => row['MFAMethods'], + name: 'MFA Methods', + sortable: true, + cell: cellGenericFormatter(), + exportSelector: 'MFAMethods', + }, + { + selector: (row) => row['CAPolicies'], + name: 'CA Policies', + sortable: true, + cell: cellGenericFormatter(), + exportSelector: 'CAPolicies', + }, ] const Altcolumns = [ From 34a75336f21626b931488cc3de1e5c8130c1d441 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 25 Jul 2024 18:20:35 -0400 Subject: [PATCH 16/52] Add remove tenant bulk action --- src/views/cipp/app-settings/SettingsTenants.jsx | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/views/cipp/app-settings/SettingsTenants.jsx b/src/views/cipp/app-settings/SettingsTenants.jsx index c425f3c1ae97..bbced7def29a 100644 --- a/src/views/cipp/app-settings/SettingsTenants.jsx +++ b/src/views/cipp/app-settings/SettingsTenants.jsx @@ -7,7 +7,7 @@ import { useLazyGenericGetRequestQuery } from 'src/store/api/app.js' import React, { useEffect, useRef } from 'react' import { ModalService, TenantSelectorMultiple } from 'src/components/utilities/index.js' import { setCurrentTenant } from 'src/store/features/app.js' -import { CAlert, CButton, CCallout, CSpinner, CTooltip } from '@coreui/react' +import { CButton, CSpinner, CTooltip } from '@coreui/react' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { faCheckCircle, @@ -261,6 +261,16 @@ export function SettingsTenants() { modalMessage: 'Are you sure you want to reset the CPV permissions for these tenants? (This will delete the Service Principal and re-add it.)', }, + { + label: 'Remove Tenant', + modal: true, + modalType: 'POST', + modalUrl: `/api/ExecRemoveTenant`, + modalBody: { + TenantID: '!customerId', + }, + modalMessage: 'Are you sure you want to remove this tenant?', + }, ], }, isModal: true, From d03b70e65725f7d40ca34ab0a9031ef5008e202d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Kj=C3=A6rg=C3=A5rd?= Date: Fri, 26 Jul 2024 00:56:57 +0200 Subject: [PATCH 17/52] Pass userEmail to backend to use in logging --- src/views/identity/administration/UserMailboxRuleList.jsx | 5 +++-- src/views/identity/administration/ViewUser.jsx | 6 +++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/views/identity/administration/UserMailboxRuleList.jsx b/src/views/identity/administration/UserMailboxRuleList.jsx index 4ab999081ee8..ae2592866e85 100644 --- a/src/views/identity/administration/UserMailboxRuleList.jsx +++ b/src/views/identity/administration/UserMailboxRuleList.jsx @@ -10,7 +10,7 @@ const rowStyle = (row, rowIndex) => { return style } -export default function UserMailboxRuleList({ userId, tenantDomain, className = null }) { +export default function UserMailboxRuleList({ userId, userEmail, tenantDomain, className = null }) { const formatter = (cell) => CellBoolean({ cell }) const columns = [ { @@ -79,7 +79,7 @@ export default function UserMailboxRuleList({ userId, tenantDomain, className = datatable={{ reportName: 'ListUserMailboxRules', path: '/api/ListUserMailboxRules', - params: { tenantFilter: tenantDomain, userId }, + params: { tenantFilter: tenantDomain, userId, userEmail }, columns, keyField: 'id', responsive: true, @@ -93,6 +93,7 @@ export default function UserMailboxRuleList({ userId, tenantDomain, className = UserMailboxRuleList.propTypes = { userId: PropTypes.string.isRequired, + userEmail: PropTypes.string, tenantDomain: PropTypes.string.isRequired, className: PropTypes.string, } diff --git a/src/views/identity/administration/ViewUser.jsx b/src/views/identity/administration/ViewUser.jsx index ae0e44510693..72341f5cda9f 100644 --- a/src/views/identity/administration/ViewUser.jsx +++ b/src/views/identity/administration/ViewUser.jsx @@ -91,7 +91,11 @@ const ViewUser = (props) => { - + )} From b55fb5fe6b871d8625650e5f68ae36078cf2e951 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Kj=C3=A6rg=C3=A5rd?= Date: Fri, 26 Jul 2024 09:36:31 +0200 Subject: [PATCH 18/52] Reorder offboarding options --- .../identity/administration/OffboardingWizard.jsx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/views/identity/administration/OffboardingWizard.jsx b/src/views/identity/administration/OffboardingWizard.jsx index d7008c071724..208190c00ee9 100644 --- a/src/views/identity/administration/OffboardingWizard.jsx +++ b/src/views/identity/administration/OffboardingWizard.jsx @@ -167,17 +167,17 @@ const OffboardingWizard = () => {
- - + + + + + + - - + - - - From 9f0bd38d3df0d2cdcadccd33f716bad4e820b4ba Mon Sep 17 00:00:00 2001 From: Esco Date: Fri, 26 Jul 2024 09:35:13 +0200 Subject: [PATCH 19/52] Added Optional Malware FileTypes --- src/data/standards.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/data/standards.json b/src/data/standards.json index dbcdd1f9a6c9..6c82f88d0fa1 100644 --- a/src/data/standards.json +++ b/src/data/standards.json @@ -1632,6 +1632,11 @@ } ] }, + { + "type": "input", + "name": "standards.MalwareFilterPolicy.OptionalFileTypes", + "label": "Optional File Types, Comma separated" + }, { "type": "Select", "label": "QuarantineTag", From 09481773764255758de794ababf759c0f5d417fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Kj=C3=A6rg=C3=A5rd?= Date: Fri, 26 Jul 2024 10:03:31 +0200 Subject: [PATCH 20/52] Add tags to standards --- src/data/standards.json | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/data/standards.json b/src/data/standards.json index dbcdd1f9a6c9..6866899224a4 100644 --- a/src/data/standards.json +++ b/src/data/standards.json @@ -584,7 +584,7 @@ { "name": "standards.OauthConsentLowSec", "cat": "Entra (AAD) Standards", - "tag": ["mediumimpact"], + "tag": ["mediumimpact", "IntegratedApps"], "helpText": "Sets the default oauth consent level so users can consent to applications that have low risks.", "docsDescription": "Allows users to consent to applications with low assigned risk.", "label": "Allow users to consent to applications with low security risk (Prevent OAuth phishing. Lower impact, less secure)", @@ -1336,7 +1336,8 @@ "mdo_highconfidencephishaction", "mdo_phisspamacation", "mdo_spam_notifications_only_for_admins", - "mdo_antiphishingpolicies" + "mdo_antiphishingpolicies", + "mdo_phishthresholdlevel" ], "helpText": "This creates a Anti-Phishing policy that automatically enables Mailbox Intelligence and spoofing, optional switches for Mailtips.", "addedComponent": [ @@ -2094,7 +2095,7 @@ { "name": "standards.DisableSharePointLegacyAuth", "cat": "SharePoint Standards", - "tag": ["mediumimpact", "CIS"], + "tag": ["mediumimpact", "CIS", "spo_legacy_auth"], "helpText": "Disables the ability to authenticate with SharePoint using legacy authentication methods. Any applications that use legacy authentication will need to be updated to use modern authentication.", "docsDescription": "Disables the ability for users and applications to access SharePoint via legacy basic authentication. This will likely not have any user impact, but will block systems/applications depending on basic auth or the SharePointOnlineCredentials class.", "addedComponent": [], From d98383f457bbbdafaf37d96fe4c612a62016fc3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Kj=C3=A6rg=C3=A5rd?= Date: Fri, 26 Jul 2024 13:23:49 +0200 Subject: [PATCH 21/52] Add missing options in user settings for Offboarding Defaults --- src/views/cipp/UserSettings.jsx | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/src/views/cipp/UserSettings.jsx b/src/views/cipp/UserSettings.jsx index cedc16b9e672..a7fcaa628f3e 100644 --- a/src/views/cipp/UserSettings.jsx +++ b/src/views/cipp/UserSettings.jsx @@ -156,28 +156,36 @@ const UserSettings = () => {

Offboarding Defaults

- - - - + - - - - - - + + + + + + + + + + +
From 0972e11eb8918ea7ad4841bafe1e7d9862e26af7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Kj=C3=A6rg=C3=A5rd?= Date: Fri, 26 Jul 2024 15:11:01 +0200 Subject: [PATCH 22/52] Add DisableAutoForwarding standard --- src/data/standards.json | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/data/standards.json b/src/data/standards.json index dbcdd1f9a6c9..a291df111cb8 100644 --- a/src/data/standards.json +++ b/src/data/standards.json @@ -1278,6 +1278,19 @@ "powershellEquivalent": "Get-Mailbox & Update-MgUser", "recommendedBy": ["CIS"] }, + { + "name": "standards.EXODisableAutoForwarding", + "cat": "Exchange Standards", + "tag": ["highimpact", "CIS", "mdo_autoforwardingmode", "mdo_blockmailforward"], + "helpText": "Disables the ability for users to automatically forward e-mails to external recipients.", + "docsDescription": "Disables the ability for users to automatically forward e-mails to external recipients. This is to prevent data exfiltration. Please check if there are any legitimate use cases for this feature before implementing, like forwarding invoices and such.", + "addedComponent": [], + "label": "Disable automatic forwarding to external recipients", + "impact": "High Impact", + "impactColour": "danger", + "powershellEquivalent": "Set-HostedOutboundSpamFilterPolicy -AutoForwardingMode 'Off'", + "recommendedBy": ["CIS"] + }, { "name": "standards.QuarantineRequestAlert", "cat": "Defender Standards", From e69f33bd55897a27206a035e652d2933932e5178 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Fri, 26 Jul 2024 10:48:43 -0400 Subject: [PATCH 23/52] Log Subscriptoins --- src/views/cipp/app-settings/CIPPSettings.jsx | 28 ++++--- .../SettingsWebhookSubscriptions.jsx | 78 +++++++++++++++++++ 2 files changed, 97 insertions(+), 9 deletions(-) create mode 100644 src/views/cipp/app-settings/SettingsWebhookSubscriptions.jsx diff --git a/src/views/cipp/app-settings/CIPPSettings.jsx b/src/views/cipp/app-settings/CIPPSettings.jsx index 5a610170e49b..b4dd85cba84f 100644 --- a/src/views/cipp/app-settings/CIPPSettings.jsx +++ b/src/views/cipp/app-settings/CIPPSettings.jsx @@ -10,6 +10,7 @@ import { SettingsNotifications } from 'src/views/cipp/app-settings/SettingsNotif import { SettingsLicenses } from 'src/views/cipp/app-settings/SettingsLicenses.jsx' import { SettingsMaintenance } from 'src/views/cipp/app-settings/SettingsMaintenance.jsx' import { SettingsPartner } from 'src/views/cipp/app-settings/SettingsPartner.jsx' +import { SettingsWebhookSubscriptions } from 'src/views/cipp/app-settings/SettingsWebhookSubscriptions.jsx' import useQuery from 'src/hooks/useQuery.jsx' import { SettingsSuperAdmin } from './SettingsSuperAdmin.jsx' import { useLoadClientPrincipalQuery } from 'src/store/api/auth.js' @@ -48,16 +49,19 @@ export default function CIPPSettings() { Notifications setActive(5)} href="#"> - Partner Webhooks + Log Subscriptions setActive(6)} href="#"> - Licenses + Partner Webhooks setActive(7)} href="#"> + Licenses + + setActive(8)} href="#"> Maintenance {superAdmin && ( - setActive(8)} href="#"> + setActive(9)} href="#"> SuperAdmin Settings )} @@ -84,23 +88,29 @@ export default function CIPPSettings() { - + - - - + + - + - + + {superAdmin && ( + + + + + + )} ) diff --git a/src/views/cipp/app-settings/SettingsWebhookSubscriptions.jsx b/src/views/cipp/app-settings/SettingsWebhookSubscriptions.jsx new file mode 100644 index 000000000000..e373644af40e --- /dev/null +++ b/src/views/cipp/app-settings/SettingsWebhookSubscriptions.jsx @@ -0,0 +1,78 @@ +import React from 'react' +import { CippPageList } from 'src/components/layout/index.js' +import { cellGenericFormatter } from 'src/components/tables/CellGenericFormat' +import { cellBadgeFormatter, cellDateFormatter } from 'src/components/tables' + +/** + * SettingsWebhookSubscriptions component is used to manage webhook subscriptions in a settings page. + * + * @returns {JSX.Element} The generated settings page component. + */ +export function SettingsWebhookSubscriptions() { + const columns = [ + { + name: 'Tenant', + selector: (row) => row['PartitionKey'], + exportSelector: 'PartitionKey', + sortable: true, + cell: cellGenericFormatter(), + }, + { + name: 'Resource', + selector: (row) => row['Resource'], + exportSelector: 'Resource', + sortable: true, + cell: cellGenericFormatter(), + }, + { + name: 'Status', + selector: (row) => row['Status'], + exportSelector: 'Status', + sortable: true, + cell: cellBadgeFormatter(), + }, + { + name: 'Last Update', + selector: (row) => row['Timestamp'], + sortable: true, + cell: cellDateFormatter({ format: 'short' }), + exportSelector: 'Timestamp', + }, + ] + return ( + <> + + + ) +} From c8aba868b3775bc3f8346dd634b227892b164222 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Fri, 26 Jul 2024 12:02:20 -0400 Subject: [PATCH 24/52] UI tweaks --- src/views/cipp/ExtensionSync.jsx | 14 +++++++------- .../app-settings/SettingsWebhookSubscriptions.jsx | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/views/cipp/ExtensionSync.jsx b/src/views/cipp/ExtensionSync.jsx index c9937921aa86..f121bf08e513 100644 --- a/src/views/cipp/ExtensionSync.jsx +++ b/src/views/cipp/ExtensionSync.jsx @@ -19,49 +19,49 @@ const ExtensionSync = () => { const columns = [ { name: 'Tenant', - selector: (row) => row['Tenant'], + selector: (row) => row?.Tenant, sortable: true, cell: cellGenericFormatter(), exportSelector: 'Tenants', }, { name: 'Sync Type', - selector: (row) => row['SyncType'], + selector: (row) => row?.SyncType, sortable: true, cell: cellBadgeFormatter({ color: 'info' }), exportSelector: 'SyncType', }, { name: 'Task', - selector: (row) => row['Name'], + selector: (row) => row?.Name, sortable: true, cell: cellGenericFormatter(), exportSelector: 'Name', }, { name: 'Scheduled Time', - selector: (row) => row['ScheduledTime'], + selector: (row) => row?.ScheduledTime, sortable: true, cell: cellDateFormatter({ format: 'short' }), exportSelector: 'ScheduledTime', }, { name: 'Last Run', - selector: (row) => row['ExecutedTime'], + selector: (row) => row?.ExecutedTime, sortable: true, cell: cellDateFormatter({ format: 'short' }), exportSelector: 'ExecutedTime', }, { name: 'Repeats every', - selector: (row) => row['RepeatsEvery'], + selector: (row) => row?.RepeatsEvery, sortable: true, cell: (row) => CellTip(row['RepeatsEvery']), exportSelector: 'RepeatsEvery', }, { name: 'Results', - selector: (row) => row['Results'], + selector: (row) => row?.Results, sortable: true, cell: cellGenericFormatter(), exportSelector: 'Results', diff --git a/src/views/cipp/app-settings/SettingsWebhookSubscriptions.jsx b/src/views/cipp/app-settings/SettingsWebhookSubscriptions.jsx index e373644af40e..965e13b57b90 100644 --- a/src/views/cipp/app-settings/SettingsWebhookSubscriptions.jsx +++ b/src/views/cipp/app-settings/SettingsWebhookSubscriptions.jsx @@ -29,7 +29,7 @@ export function SettingsWebhookSubscriptions() { selector: (row) => row['Status'], exportSelector: 'Status', sortable: true, - cell: cellBadgeFormatter(), + cell: cellBadgeFormatter({ color: 'info' }), }, { name: 'Last Update', From a0eb3ecc93452db449303438e2d7af6abd4e7748 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Fri, 26 Jul 2024 13:01:35 -0400 Subject: [PATCH 25/52] up version --- package.json | 2 +- public/version_latest.txt | 2 +- version_latest.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 0d19ddc025c7..e3b9958fe19e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cipp", - "version": "6.1.0", + "version": "6.1.1", "description": "The CyberDrain Improved Partner Portal is a portal to help manage administration for Microsoft Partners.", "homepage": "https://cipp.app/", "bugs": { diff --git a/public/version_latest.txt b/public/version_latest.txt index dfda3e0b4f01..f3b5af39e430 100644 --- a/public/version_latest.txt +++ b/public/version_latest.txt @@ -1 +1 @@ -6.1.0 +6.1.1 diff --git a/version_latest.txt b/version_latest.txt index dfda3e0b4f01..f3b5af39e430 100644 --- a/version_latest.txt +++ b/version_latest.txt @@ -1 +1 @@ -6.1.0 +6.1.1 From 8c7296f3d0c452cf4698cbd754983df37752449d Mon Sep 17 00:00:00 2001 From: Esco Date: Sat, 27 Jul 2024 16:54:20 +0200 Subject: [PATCH 26/52] Added Teams Global Meeting Policy standard --- src/data/standards.json | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/data/standards.json b/src/data/standards.json index 63f9c47d07a2..f49e96a91139 100644 --- a/src/data/standards.json +++ b/src/data/standards.json @@ -2302,5 +2302,41 @@ "impactColour": "danger", "powershellEquivalent": "Update-MgAdminSharepointSetting", "recommendedBy": [] + }, + { + "name": "standards.TeamsGlobalMeetingPolicy", + "cat": "Teams Standards", + "tag": ["lowimpact"], + "helpText": "Defines the CIS recommended global meeting policy for Teams. This includes AllowAnonymousUsersToJoinMeeting, AllowAnonymousUsersToStartMeeting, AutoAdmittedUsers, AllowPSTNUsersToBypassLobby, MeetingChatEnabledType, DesignatedPresenterRoleMode, AllowExternalParticipantGiveRequestControl", + "addedComponent": [ + { + "type": "Select", + "name": "standards.TeamsGlobalMeetingPolicy.DesignatedPresenterRoleMode", + "label": "Default value of the `Who can present?`", + "values": [ + { + "label": "EveryoneUserOverride", + "value": "EveryoneUserOverride" + }, + { + "label": "EveryoneInCompanyUserOverride", + "value": "EveryoneInCompanyUserOverride" + }, + { + "label": "EveryoneInSameAndFederatedCompanyUserOverride", + "value": "EveryoneInSameAndFederatedCompanyUserOverride" + }, + { + "label": "OrganizerOnlyUserOverride", + "value": "OrganizerOnlyUserOverride" + } + ] + } + ], + "label": "Define Global Meeting Policy for Teams", + "impact": "Low Impact", + "impactColour": "info", + "powershellEquivalent": "Set-CsTeamsMeetingPolicy -AllowAnonymousUsersToJoinMeeting $false -AllowAnonymousUsersToStartMeeting $false -AutoAdmittedUsers EveryoneInCompanyExcludingGuests -AllowPSTNUsersToBypassLobby $false -MeetingChatEnabledType EnabledExceptAnonymous -DesignatedPresenterRoleMode $DesignatedPresenterRoleMode -AllowExternalParticipantGiveRequestControl $false", + "recommendedBy": ["CIS 3.0"] } ] From 9cb5b30a882d18fd30455a811e068bdb88909952 Mon Sep 17 00:00:00 2001 From: Esco Date: Sat, 27 Jul 2024 10:12:34 +0200 Subject: [PATCH 27/52] Added Teams External File Sharing Standard --- src/data/standards.json | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/src/data/standards.json b/src/data/standards.json index f49e96a91139..51e5cf143234 100644 --- a/src/data/standards.json +++ b/src/data/standards.json @@ -2338,5 +2338,43 @@ "impactColour": "info", "powershellEquivalent": "Set-CsTeamsMeetingPolicy -AllowAnonymousUsersToJoinMeeting $false -AllowAnonymousUsersToStartMeeting $false -AutoAdmittedUsers EveryoneInCompanyExcludingGuests -AllowPSTNUsersToBypassLobby $false -MeetingChatEnabledType EnabledExceptAnonymous -DesignatedPresenterRoleMode $DesignatedPresenterRoleMode -AllowExternalParticipantGiveRequestControl $false", "recommendedBy": ["CIS 3.0"] + }, + { + "name": "standards.TeamsExternalFileSharing", + "cat": "Teams Standards", + "tag": ["lowimpact"], + "helpText": "Ensure external file sharing in Teams is enabled for only approved cloud storage services.", + "addedComponent": [ + { + "type": "boolean", + "name": "standards.TeamsExternalFileSharing.AllowGoogleDrive", + "label": "Allow Google Drive" + }, + { + "type": "boolean", + "name": "standards.TeamsExternalFileSharing.AllowShareFile", + "label": "Allow ShareFile" + }, + { + "type": "boolean", + "name": "standards.TeamsExternalFileSharing.AllowBox", + "label": "Allow Box" + }, + { + "type": "boolean", + "name": "standards.TeamsExternalFileSharing.AllowDropBox", + "label": "Allow Dropbox" + }, + { + "type": "boolean", + "name": "standards.TeamsExternalFileSharing.AllowEgnyte", + "label": "Allow Egnyte" + } + ], + "label": "Define approved cloud storage services for external file sharing in Teams", + "impact": "Low Impact", + "impactColour": "info", + "powershellEquivalent": "Set-CsTeamsClientConfiguration -AllowGoogleDrive $false -AllowShareFile $false -AllowBox $false -AllowDropBox $false -AllowEgnyte $false", + "recommendedBy": ["CIS 3.0"] } ] From 3a8c74e4dac88826569334823958b4eecc246c05 Mon Sep 17 00:00:00 2001 From: cipptesting Date: Mon, 29 Jul 2024 14:09:02 -0400 Subject: [PATCH 28/52] Updated Spam Filter Standard --- src/data/standards.json | 51 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/src/data/standards.json b/src/data/standards.json index 51e5cf143234..1536d4a20a2c 100644 --- a/src/data/standards.json +++ b/src/data/standards.json @@ -1703,6 +1703,12 @@ "tag": ["mediumimpact"], "helpText": "This standard creates a Spam filter policy similar to the default strict policy.", "addedComponent": [ + { + "type": "number", + "label": "Bulk email threshold (Default 7)", + "name": "standards.SpamFilterPolicy.BulkThreshold", + "default": 7 + }, { "type": "Select", "label": "Spam Action", @@ -1737,6 +1743,21 @@ } ] }, + { + "type": "Select", + "label": "High Confidence Spam Action", + "name": "standards.SpamFilterPolicy.HighConfidenceSpamAction", + "values": [ + { + "label": "Quarantine the message", + "value": "Quarantine" + }, + { + "label": "Move message to Junk Email folder", + "value": "MoveToJmf" + } + ] + }, { "type": "Select", "label": "High Confidence Spam Quarantine Tag", @@ -1756,6 +1777,21 @@ } ] }, + { + "type": "Select", + "label": "Bulk Spam Action", + "name": "standards.SpamFilterPolicy.BulkSpamAction", + "values": [ + { + "label": "Move message to Junk Email folder", + "value": "MoveToJmf" + }, + { + "label": "Quarantine the message", + "value": "Quarantine" + } + ] + }, { "type": "Select", "label": "Bulk Quarantine Tag", @@ -1775,6 +1811,21 @@ } ] }, + { + "type": "Select", + "label": "Phish Spam Action", + "name": "standards.SpamFilterPolicy.PhishSpamAction", + "values": [ + { + "label": "Quarantine the message", + "value": "Quarantine" + }, + { + "label": "Move message to Junk Email folder", + "value": "MoveToJmf" + } + ] + }, { "type": "Select", "label": "Phish Quarantine Tag", From e2cbb688cd480fcef6f306a784cc7dd73ac6570d Mon Sep 17 00:00:00 2001 From: cipptesting Date: Mon, 29 Jul 2024 15:37:09 -0400 Subject: [PATCH 29/52] Updated standards.json based on feedback --- src/data/standards.json | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/data/standards.json b/src/data/standards.json index 1536d4a20a2c..310890368c4d 100644 --- a/src/data/standards.json +++ b/src/data/standards.json @@ -1714,13 +1714,13 @@ "label": "Spam Action", "name": "standards.SpamFilterPolicy.SpamAction", "values": [ - { - "label": "Move message to Junk Email folder", - "value": "MoveToJmf" - }, { "label": "Quarantine the message", "value": "Quarantine" + }, + { + "label": "Move message to Junk Email folder", + "value": "MoveToJmf" } ] }, @@ -1782,13 +1782,13 @@ "label": "Bulk Spam Action", "name": "standards.SpamFilterPolicy.BulkSpamAction", "values": [ - { - "label": "Move message to Junk Email folder", - "value": "MoveToJmf" - }, { "label": "Quarantine the message", "value": "Quarantine" + }, + { + "label": "Move message to Junk Email folder", + "value": "MoveToJmf" } ] }, From 2bb4af823c38069b47055766433377cd35905402 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Mon, 29 Jul 2024 17:38:13 -0400 Subject: [PATCH 30/52] Add controlStateUpdates table to Secure Score page --- .../tenant/administration/SecureScore.jsx | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/views/tenant/administration/SecureScore.jsx b/src/views/tenant/administration/SecureScore.jsx index 76c77d584296..be036997339c 100644 --- a/src/views/tenant/administration/SecureScore.jsx +++ b/src/views/tenant/administration/SecureScore.jsx @@ -15,7 +15,7 @@ import { CRow, } from '@coreui/react' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' -import { faCheck, faTimes, faExclamation } from '@fortawesome/free-solid-svg-icons' +import { faCheck, faTimes } from '@fortawesome/free-solid-svg-icons' import { CippTable } from 'src/components/tables' import { CippPage } from 'src/components/layout/CippPage' import { useGenericGetRequestQuery, useLazyGenericPostRequestQuery } from 'src/store/api/app' @@ -27,6 +27,7 @@ import { ModalService } from 'src/components/utilities' import { CellTip, cellGenericFormatter } from 'src/components/tables/CellGenericFormat' import { CippCallout } from 'src/components/layout' import CippPrettyCard from 'src/components/contentcards/CippPrettyCard' +import { TableModalButton } from 'src/components/buttons' const SecureScore = () => { const textRef = useRef() @@ -192,6 +193,11 @@ const SecureScore = () => { cell: cellGenericFormatter(), exportSelector: 'actionUrl', }, + { + name: 'Updates', + selector: (row) => row?.controlStateUpdates, + cell: cellGenericFormatter(), + }, ] return ( @@ -278,7 +284,7 @@ const SecureScore = () => {
- {viewMode && translateData.controlScores.length > 1 && isSuccess && isSuccessTranslation && ( + {viewMode && translateData.controlScores?.length > 1 && isSuccess && isSuccessTranslation && ( Best Practice Report @@ -286,7 +292,7 @@ const SecureScore = () => { { openResolution(info)} className="me-3"> Change Status + + From 68d6e5cec6b03ae9c51fd019347d8c1520223e49 Mon Sep 17 00:00:00 2001 From: Esco Date: Tue, 30 Jul 2024 10:11:56 +0200 Subject: [PATCH 31/52] Added Teams Email Integration standard --- src/data/standards.json | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/data/standards.json b/src/data/standards.json index 51e5cf143234..f4e4e8f28b5f 100644 --- a/src/data/standards.json +++ b/src/data/standards.json @@ -2339,6 +2339,25 @@ "powershellEquivalent": "Set-CsTeamsMeetingPolicy -AllowAnonymousUsersToJoinMeeting $false -AllowAnonymousUsersToStartMeeting $false -AutoAdmittedUsers EveryoneInCompanyExcludingGuests -AllowPSTNUsersToBypassLobby $false -MeetingChatEnabledType EnabledExceptAnonymous -DesignatedPresenterRoleMode $DesignatedPresenterRoleMode -AllowExternalParticipantGiveRequestControl $false", "recommendedBy": ["CIS 3.0"] }, + { + "name": "standards.TeamsEmailIntegration", + "cat": "Teams Standards", + "tag": ["lowimpact"], + "helpText": "Should users be allowed to send emails directly to a channel email addresses?", + "docsDescription": "Teams channel email addresses are an optional feature that allows users to email the Teams channel directly.", + "addedComponent": [ + { + "type": "boolean", + "name": "standards.TeamsEmailIntegration.AllowEmailIntoChannel", + "label": "Allow channel emails" + } + ], + "label": "Disallow emails to be sent to channel email addresses", + "impact": "Low Impact", + "impactColour": "info", + "powershellEquivalent": "Set-CsTeamsClientConfiguration -AllowEmailIntoChannel $false", + "recommendedBy": ["CIS 3.0"] + }, { "name": "standards.TeamsExternalFileSharing", "cat": "Teams Standards", From 562e40f39302f1a625d0fae7fbb8017c76444a11 Mon Sep 17 00:00:00 2001 From: Esco Date: Sat, 27 Jul 2024 23:47:50 +0200 Subject: [PATCH 32/52] Added Teams External Access Policy Standard --- src/data/standards.json | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/data/standards.json b/src/data/standards.json index 51e5cf143234..4850180e2bee 100644 --- a/src/data/standards.json +++ b/src/data/standards.json @@ -2376,5 +2376,34 @@ "impactColour": "info", "powershellEquivalent": "Set-CsTeamsClientConfiguration -AllowGoogleDrive $false -AllowShareFile $false -AllowBox $false -AllowDropBox $false -AllowEgnyte $false", "recommendedBy": ["CIS 3.0"] + }, + { + "name": "standards.TeamsExternalAccessPolicy", + "cat": "Teams Standards", + "tag": ["mediumimpact"], + "helpText": "Sets the properties of the Global external access policy.", + "docsDescription": "Sets the properties of the Global external access policy. External access policies determine whether or not your users can: 1) communicate with users who have Session Initiation Protocol (SIP) accounts with a federated organization; 2) communicate with users who are using custom applications built with Azure Communication Services; 3) access Skype for Business Server over the Internet, without having to log on to your internal network; 4) communicate with users who have SIP accounts with a public instant messaging (IM) provider such as Skype; and, 5) communicate with people who are using Teams with an account that's not managed by an organization.", + "addedComponent": [ + { + "type": "boolean", + "name": "standards.TeamsExternalAccessPolicy.EnableFederationAccess", + "label": "Allow communication from trusted organizations" + }, + { + "type": "boolean", + "name": "standards.TeamsExternalAccessPolicy.EnablePublicCloudAccess", + "label": "Allow user to communicate with Skype users" + }, + { + "type": "boolean", + "name": "standards.TeamsExternalAccessPolicy.EnableTeamsConsumerAccess", + "label": "Allow communication with unmanaged Teams accounts" + } + ], + "label": "External Access Settings for Microsoft Teams", + "impact": "Medium Impact", + "impactColour": "warning", + "powershellEquivalent": "Set-CsExternalAccessPolicy", + "recommendedBy": [] } ] From 4ff96472ac6f94f127c68337506e00e1c1fa931e Mon Sep 17 00:00:00 2001 From: John Duprey Date: Tue, 30 Jul 2024 15:31:04 -0400 Subject: [PATCH 33/52] Add form validation for GDAP invite wizard Remove the ability to have duplicate roleDefinitionIds selected --- .../administration/GDAPInviteWizard.jsx | 28 +++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/src/views/tenant/administration/GDAPInviteWizard.jsx b/src/views/tenant/administration/GDAPInviteWizard.jsx index 5f303a3c827f..e46c6b7180e7 100644 --- a/src/views/tenant/administration/GDAPInviteWizard.jsx +++ b/src/views/tenant/administration/GDAPInviteWizard.jsx @@ -28,7 +28,7 @@ const Error = ({ name }) => ( render={({ meta: { touched, error } }) => touched && error ? ( - + {error} ) : null @@ -40,7 +40,31 @@ Error.propTypes = { name: PropTypes.string.isRequired, } -const requiredArray = (value) => (value && value.length !== 0 ? undefined : 'Required') +const requiredArray = (value) => { + if (value && value.length !== 0) { + /// group each item in value by roleDefinitionId and select Role name where count is greater than 1 + const duplicateRoles = value + .map((item) => item.roleDefinitionId) + .filter((item, index, self) => index !== self.indexOf(item)) + console.log(duplicateRoles) + + if (duplicateRoles.length > 0) { + var duplicates = value.filter((item) => duplicateRoles.includes(item.roleDefinitionId)) + /// get unique list of duplicate roles + duplicates = duplicates + .filter( + (role, index, self) => + index === self.findIndex((t) => t.roleDefinitionId === role.roleDefinitionId), + ) + .map((role) => role.RoleName) + return `Duplicate GDAP Roles selected, remove one of the mapped groups for the listed roles to continue: ${duplicates}` + } else { + return undefined + } + } else { + return 'You must select at least one GDAP Role' + } +} const GDAPInviteWizard = () => { const defaultRolesArray = [ From da771ee9d0414d95c18b6e70313e7e3a246e53be Mon Sep 17 00:00:00 2001 From: John Duprey Date: Tue, 30 Jul 2024 16:22:26 -0400 Subject: [PATCH 34/52] Add GDAP invite page --- src/_nav.jsx | 7 ++- src/importsMap.jsx | 3 +- src/routes.json | 8 ++- .../tenant/administration/ListGDAPInvites.jsx | 63 +++++++++++++++++++ 4 files changed, 78 insertions(+), 3 deletions(-) create mode 100644 src/views/tenant/administration/ListGDAPInvites.jsx diff --git a/src/_nav.jsx b/src/_nav.jsx index c0faa91279c0..c83f7f179148 100644 --- a/src/_nav.jsx +++ b/src/_nav.jsx @@ -326,7 +326,12 @@ const _nav = [ { component: CNavItem, name: 'Invite Wizard', - to: '/tenant/administration/gdap-invite', + to: '/tenant/administration/gdap-invite-wizard', + }, + { + component: CNavItem, + name: 'Invite List', + to: '/tenant/administration/gdap-invites', }, { component: CNavItem, diff --git a/src/importsMap.jsx b/src/importsMap.jsx index eaa260ad4dbd..21174e32a0f2 100644 --- a/src/importsMap.jsx +++ b/src/importsMap.jsx @@ -147,7 +147,8 @@ import React from 'react' "/cipp/setup": React.lazy(() => import('./views/cipp/Setup')), "/tenant/administration/securescore": React.lazy(() => import('./views/tenant/administration/SecureScore')), "/tenant/administration/gdap": React.lazy(() => import('./views/tenant/administration/GDAPWizard')), - "/tenant/administration/gdap-invite": React.lazy(() => import('./views/tenant/administration/GDAPInviteWizard')), + "/tenant/administration/gdap-invite-wizard": React.lazy(() => import('./views/tenant/administration/GDAPInviteWizard')), + "/tenant/administration/gdap-invites": React.lazy(() => import('./views/tenant/administration/ListGDAPInvites')), "/tenant/administration/gdap-role-wizard": React.lazy(() => import('./views/tenant/administration/GDAPRoleWizard')), "/tenant/administration/gdap-roles": React.lazy(() => import('./views/tenant/administration/ListGDAPRoles')), "/tenant/administration/gdap-relationships": React.lazy(() => import('././views/tenant/administration/ListGDAPRelationships')), diff --git a/src/routes.json b/src/routes.json index f5b6053359a7..c0a996610e41 100644 --- a/src/routes.json +++ b/src/routes.json @@ -999,11 +999,17 @@ "allowedRoles": ["admin"] }, { - "path": "/tenant/administration/gdap-invite", + "path": "/tenant/administration/gdap-invite-wizard", "name": "GDAP Invite Wizard", "component": "views/tenant/administration/GDAPInviteWizard", "allowedRoles": ["admin"] }, + { + "path": "/tenant/administration/gdap-invites", + "name": "GDAP Invites", + "component": "views/tenant/administration/ListGDAPInvites", + "allowedRoles": ["admin"] + }, { "path": "/tenant/administration/gdap-role-wizard", "name": "GDAP Role Wizard", diff --git a/src/views/tenant/administration/ListGDAPInvites.jsx b/src/views/tenant/administration/ListGDAPInvites.jsx new file mode 100644 index 000000000000..24a44e6b253f --- /dev/null +++ b/src/views/tenant/administration/ListGDAPInvites.jsx @@ -0,0 +1,63 @@ +import React from 'react' +import { CippPageList } from 'src/components/layout' +import { TitleButton } from 'src/components/buttons' +import { cellGenericFormatter } from 'src/components/tables/CellGenericFormat' +import { cellDateFormatter } from 'src/components/tables' + +const ListGDAPInvites = () => { + const columns = [ + { + name: 'Created', + selector: (row) => row['Timestamp'], + sortable: true, + exportSelector: 'Timestamp', + cell: cellDateFormatter({ format: 'short' }), + }, + { + name: 'Relationship ID', + selector: (row) => row['RowKey'], + sortable: true, + exportSelector: 'RowKey', + cell: cellGenericFormatter(), + }, + { + name: 'Invite URL', + selector: (row) => row['InviteUrl'], + exportSelector: 'InviteUrl', + cell: cellGenericFormatter(), + }, + { + name: 'Onboarding URL', + selector: (row) => row['OnboardingUrl'], + exportSelector: 'OnboardingUrl', + cell: cellGenericFormatter(), + }, + { + name: 'Role Mapping', + selector: (row) => row['RoleMappings'], + exportSelector: 'RoleMappings', + cell: cellGenericFormatter(), + }, + ] + return ( +
+ +
+ ) +} + +export default ListGDAPInvites From 4b18fc4b1d089f6ea9c5bfbe38f66fb5eb9225f1 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Tue, 30 Jul 2024 16:37:15 -0400 Subject: [PATCH 35/52] wording, remove console log --- src/views/tenant/administration/GDAPInviteWizard.jsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/views/tenant/administration/GDAPInviteWizard.jsx b/src/views/tenant/administration/GDAPInviteWizard.jsx index e46c6b7180e7..a3c38dbf7dc6 100644 --- a/src/views/tenant/administration/GDAPInviteWizard.jsx +++ b/src/views/tenant/administration/GDAPInviteWizard.jsx @@ -46,18 +46,20 @@ const requiredArray = (value) => { const duplicateRoles = value .map((item) => item.roleDefinitionId) .filter((item, index, self) => index !== self.indexOf(item)) - console.log(duplicateRoles) if (duplicateRoles.length > 0) { var duplicates = value.filter((item) => duplicateRoles.includes(item.roleDefinitionId)) /// get unique list of duplicate roles + duplicates = duplicates .filter( (role, index, self) => index === self.findIndex((t) => t.roleDefinitionId === role.roleDefinitionId), ) .map((role) => role.RoleName) - return `Duplicate GDAP Roles selected, remove one of the mapped groups for the listed roles to continue: ${duplicates}` + return `Duplicate GDAP Roles selected, ensure there is only one group mapping for the listed roles to continue: ${duplicates.join( + ', ', + )}` } else { return undefined } From a6aa56c511c426b8bd58e2e72466e2f46ce1b91d Mon Sep 17 00:00:00 2001 From: Esco Date: Sun, 28 Jul 2024 00:52:21 +0200 Subject: [PATCH 36/52] Added Teams Federation Configuration Standard --- src/data/standards.json | 52 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/src/data/standards.json b/src/data/standards.json index 9667715a5607..ef14f236cd1a 100644 --- a/src/data/standards.json +++ b/src/data/standards.json @@ -2475,5 +2475,57 @@ "impactColour": "warning", "powershellEquivalent": "Set-CsExternalAccessPolicy", "recommendedBy": [] + }, + { + "name": "standards.TeamsFederationConfiguration", + "cat": "Teams Standards", + "tag": ["mediumimpact"], + "helpText": "Sets the properties of the Global federation configuration.", + "docsDescription": "Sets the properties of the Global federation configuration. Federation configuration settings determine whether or not your users can communicate with users who have SIP accounts with a federated organization.", + "addedComponent": [ + { + "type": "boolean", + "name": "standards.TeamsFederationConfiguration.AllowTeamsConsumer", + "label": "Allow users to communicate with other organizations" + }, + { + "type": "boolean", + "name": "standards.TeamsFederationConfiguration.AllowPublicUsers", + "label": "Allow users to communicate with Skype Users" + }, + { + "type": "Select", + "name": "standards.TeamsFederationConfiguration.DomainControl", + "label": "Communication Mode", + "values": [ + { + "label": "Allow all external domains", + "value": "AllowAllExternal" + }, + { + "label": "Block all external domains", + "value": "BlockAllExternal" + }, + { + "label": "Allow specific external domains", + "value": "AllowSpecificExternal" + }, + { + "label": "Block specific external domains", + "value": "BlockSpecificExternal" + } + ] + }, + { + "type": "input", + "name": "standards.TeamsFederationConfiguration.DomainList", + "label": "Domains, Comma separated" + } + ], + "label": "Federation Configuration for Microsoft Teams", + "impact": "Medium Impact", + "impactColour": "warning", + "powershellEquivalent": "Set-CsTenantFederationConfiguration", + "recommendedBy": [] } ] From b168c995bafc24a891d6d29b5f634f49efed8974 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Wed, 31 Jul 2024 16:18:06 -0400 Subject: [PATCH 37/52] Message Viewer --- package-lock.json | 188 ++++++++++- package.json | 6 +- src/_nav.jsx | 5 + src/components/utilities/CippDropzone.jsx | 78 +++++ src/importsMap.jsx | 1 + src/routes.json | 6 + .../email-exchange/tools/MessageViewer.jsx | 310 ++++++++++++++++++ 7 files changed, 582 insertions(+), 12 deletions(-) create mode 100644 src/components/utilities/CippDropzone.jsx create mode 100644 src/views/email-exchange/tools/MessageViewer.jsx diff --git a/package-lock.json b/package-lock.json index bc2ed9783377..5148294a75a6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "cipp", - "version": "5.8.5", + "version": "6.1.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "cipp", - "version": "5.8.5", + "version": "6.1.1", "license": "AGPL-3.0", "dependencies": { "@coreui/chartjs": "^3.0.0", @@ -32,6 +32,8 @@ "chart.js": "^3.5.1", "classnames": "^2.3.1", "core-js": "^3.18.3", + "dompurify": "^3.1.6", + "eml-parse-js": "^1.1.14", "enzyme": "^3.11.0", "final-form": "^4.20.4", "final-form-arrays": "^3.1.0", @@ -51,11 +53,13 @@ "react-data-table-component": "^7.4.5", "react-datepicker": "^4.10.0", "react-dom": "^18.2.0", + "react-dropzone": "^14.2.3", "react-final-form": "^6.5.9", "react-final-form-arrays": "^3.1.4", "react-final-form-listeners": "^1.0.3", "react-helmet-async": "^1.3.0", "react-hotkeys-hook": "^3.4.4", + "react-html-parser": "^2.0.2", "react-loading-skeleton": "^3.1.0", "react-masonry-component": "^6.3.0", "react-media-hook": "^0.4.9", @@ -70,7 +74,7 @@ "redux-persist": "^6.0.0", "simplebar-react": "^2.3.6", "source-map-loader": "^3.0.0", - "styled-components": "^5.3.3" + "styled-components": "^5.3.11" }, "devDependencies": { "@types/react": "^18.2.39", @@ -1714,6 +1718,11 @@ "win32" ] }, + "node_modules/@sinonjs/text-encoding": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.2.tgz", + "integrity": "sha512-sXXKG+uL9IrKqViTtao2Ws6dy0znu9sOaP1di/jKGW1M6VssO8vlpXCQcpZ+jisQ1tTFAC5Jo/EOzFbggBagFQ==" + }, "node_modules/@types/babel__core": { "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", @@ -2438,6 +2447,14 @@ "node": ">= 4.5.0" } }, + "node_modules/attr-accept": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/attr-accept/-/attr-accept-2.2.2.tgz", + "integrity": "sha512-7prDjvt9HmqiZ0cl5CRjtS84sEyhsHP2coDkaZKRKVfCDo9s7iw7ChVmar78Gu9pC4SoR/28wFu/G5JJhTnqEg==", + "engines": { + "node": ">=4" + } + }, "node_modules/auto-changelog": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/auto-changelog/-/auto-changelog-2.3.0.tgz", @@ -3331,10 +3348,9 @@ } }, "node_modules/dompurify": { - "version": "2.4.7", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.4.7.tgz", - "integrity": "sha512-kxxKlPEDa6Nc5WJi+qRgPbOAbgTpSULL+vI3NUXsZMlkJxTqYI9wg5ZTay2sFrdZRWHPWNi+EdAhcJf81WtoMQ==", - "optional": true + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.1.6.tgz", + "integrity": "sha512-cTOAhc36AalkjtBpfG6O8JimdTMWNXjiePT2xQH/ppBGi/4uIpmj8eKyIkMJErXWARyINV/sB38yf8JCLF5pbQ==" }, "node_modules/domutils": { "version": "3.1.0", @@ -3362,6 +3378,15 @@ "batch-processor": "1.0.0" } }, + "node_modules/eml-parse-js": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/eml-parse-js/-/eml-parse-js-1.1.14.tgz", + "integrity": "sha512-6wUmZQ4k67CHGaQdNTukUMtCQ77e/676pRRsn/ga6CdaIwitzbQwqA/YTq/Wk+l1gghFJTPhbRyQphrAptK/GA==", + "dependencies": { + "@sinonjs/text-encoding": "^0.7.2", + "js-base64": "^3.7.2" + } + }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -4259,6 +4284,17 @@ "node": "^10.12.0 || >=12.0.0" } }, + "node_modules/file-selector": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/file-selector/-/file-selector-0.6.0.tgz", + "integrity": "sha512-QlZ5yJC0VxHxQQsQhXvBaC7VRJ2uaxTf+Tfpu4Z/OcVQJVpZO+DGU0rkoVW5ce2SccxugvpBJoMvUs59iILYdw==", + "dependencies": { + "tslib": "^2.4.0" + }, + "engines": { + "node": ">= 12" + } + }, "node_modules/fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -5031,8 +5067,7 @@ "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "node_modules/ini": { "version": "1.3.8", @@ -5537,6 +5572,11 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/js-base64": { + "version": "3.7.7", + "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-3.7.7.tgz", + "integrity": "sha512-7rCnleh0z2CkXhH67J8K1Ytz0b2Y+yxTPL+/KOJoa20hfnVQ/3/T6W/KflYI4bRHRagNeXeU2bkNGI3v1oS/lw==" + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -5658,6 +5698,12 @@ "jspdf": "^2.5.1" } }, + "node_modules/jspdf/node_modules/dompurify": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.5.6.tgz", + "integrity": "sha512-zUTaUBO8pY4+iJMPE1B9XlO2tXVYIcEA4SNGtvDELzTSCQO7RzH+j7S180BmhmJId78lqGU2z19vgVx2Sxs/PQ==", + "optional": true + }, "node_modules/jsx-ast-utils": { "version": "3.3.5", "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", @@ -6931,6 +6977,22 @@ "react": "^18.2.0" } }, + "node_modules/react-dropzone": { + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/react-dropzone/-/react-dropzone-14.2.3.tgz", + "integrity": "sha512-O3om8I+PkFKbxCukfIR3QAGftYXDZfOE2N1mr/7qebQJHs7U+/RSL/9xomJNpRg9kM5h9soQSdf0Gc7OHF5Fug==", + "dependencies": { + "attr-accept": "^2.2.2", + "file-selector": "^0.6.0", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">= 10.13" + }, + "peerDependencies": { + "react": ">= 16.8 || 18.0.0" + } + }, "node_modules/react-fast-compare": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz", @@ -7008,6 +7070,85 @@ "react-dom": ">=16.8.1" } }, + "node_modules/react-html-parser": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/react-html-parser/-/react-html-parser-2.0.2.tgz", + "integrity": "sha512-XeerLwCVjTs3njZcgCOeDUqLgNIt/t+6Jgi5/qPsO/krUWl76kWKXMeVs2LhY2gwM6X378DkhLjur0zUQdpz0g==", + "dependencies": { + "htmlparser2": "^3.9.0" + }, + "peerDependencies": { + "react": "^0.14.0 || ^15.0.0 || ^16.0.0-0" + } + }, + "node_modules/react-html-parser/node_modules/dom-serializer": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", + "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==", + "dependencies": { + "domelementtype": "^2.0.1", + "entities": "^2.0.0" + } + }, + "node_modules/react-html-parser/node_modules/dom-serializer/node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/react-html-parser/node_modules/dom-serializer/node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/react-html-parser/node_modules/domelementtype": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==" + }, + "node_modules/react-html-parser/node_modules/domhandler": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", + "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", + "dependencies": { + "domelementtype": "1" + } + }, + "node_modules/react-html-parser/node_modules/domutils": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", + "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", + "dependencies": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, + "node_modules/react-html-parser/node_modules/entities": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", + "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==" + }, + "node_modules/react-html-parser/node_modules/htmlparser2": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", + "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==", + "dependencies": { + "domelementtype": "^1.3.1", + "domhandler": "^2.3.0", + "domutils": "^1.5.1", + "entities": "^1.1.1", + "inherits": "^2.0.1", + "readable-stream": "^3.1.1" + } + }, "node_modules/react-is": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", @@ -7393,6 +7534,19 @@ "node": ">=8" } }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -7699,8 +7853,7 @@ "type": "consulting", "url": "https://feross.org/support" } - ], - "peer": true + ] }, "node_modules/safe-regex-test": { "version": "1.0.3", @@ -8079,6 +8232,14 @@ "resolved": "https://registry.npmjs.org/state-local/-/state-local-1.0.7.tgz", "integrity": "sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w==" }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -8595,6 +8756,11 @@ "json5": "lib/cli.js" } }, + "node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", diff --git a/package.json b/package.json index e3b9958fe19e..169a8fbc8a05 100644 --- a/package.json +++ b/package.json @@ -50,6 +50,8 @@ "chart.js": "^3.5.1", "classnames": "^2.3.1", "core-js": "^3.18.3", + "dompurify": "^3.1.6", + "eml-parse-js": "^1.1.14", "enzyme": "^3.11.0", "final-form": "^4.20.4", "final-form-arrays": "^3.1.0", @@ -69,11 +71,13 @@ "react-data-table-component": "^7.4.5", "react-datepicker": "^4.10.0", "react-dom": "^18.2.0", + "react-dropzone": "^14.2.3", "react-final-form": "^6.5.9", "react-final-form-arrays": "^3.1.4", "react-final-form-listeners": "^1.0.3", "react-helmet-async": "^1.3.0", "react-hotkeys-hook": "^3.4.4", + "react-html-parser": "^2.0.2", "react-loading-skeleton": "^3.1.0", "react-masonry-component": "^6.3.0", "react-media-hook": "^0.4.9", @@ -88,7 +92,7 @@ "redux-persist": "^6.0.0", "simplebar-react": "^2.3.6", "source-map-loader": "^3.0.0", - "styled-components": "^5.3.3" + "styled-components": "^5.3.11" }, "devDependencies": { "@types/react": "^18.2.39", diff --git a/src/_nav.jsx b/src/_nav.jsx index c83f7f179148..59d2980e2ae1 100644 --- a/src/_nav.jsx +++ b/src/_nav.jsx @@ -687,6 +687,11 @@ const _nav = [ name: 'Mail Test', to: '/email/tools/mail-test', }, + { + component: CNavItem, + name: 'Message Viewer', + to: '/email/tools/message-viewer', + }, ], }, { diff --git a/src/components/utilities/CippDropzone.jsx b/src/components/utilities/CippDropzone.jsx new file mode 100644 index 000000000000..e42ab066ae6f --- /dev/null +++ b/src/components/utilities/CippDropzone.jsx @@ -0,0 +1,78 @@ +import React, { useCallback, useMemo, useState } from 'react' +import PropTypes from 'prop-types' +import { CippContentCard } from 'src/components/layout' +import { useDropzone } from 'react-dropzone' +import styled from 'styled-components' +import { useMediaPredicate } from 'react-media-hook' +import { useSelector } from 'react-redux' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' + +const getColor = (props) => { + if (props.isDragAccept) { + return '#00e676' + } + if (props.isDragReject) { + return '#ff1744' + } + if (props.isFocused) { + return '#2196f3' + } + return '#eeeeee' +} + +const BackgroundColor = () => { + const currentTheme = useSelector((state) => state.app.currentTheme) + const preferredTheme = useMediaPredicate('(prefers-color-scheme: dark)') ? 'impact' : 'cyberdrain' + const isDark = + currentTheme === 'impact' || (currentTheme === 'default' && preferredTheme === 'impact') + + if (isDark) { + return '#333' + } else { + return '#fafafa' + } +} + +const Container = styled.div` + flex: 1; + display: flex; + flex-direction: column; + align-items: center; + padding: 20px; + border-width: 2px; + border-radius: 2px; + border-color: ${(props) => getColor(props)}; + border-style: dashed; + background-color: ${() => BackgroundColor()}; + color: #bdbdbd; + outline: none; + transition: border 0.24s ease-in-out; +` + +const CippDropzone = ({ title, onDrop, dropMessage, accept, maxFiles = 1, ...props }) => { + const { getRootProps, getInputProps, isFocused, isDragAccept, isDragReject } = useDropzone({ + onDrop, + accept: accept, + maxFiles: maxFiles, + }) + return ( + +
+ + + {dropMessage} + +
+
+ ) +} + +CippDropzone.propTypes = { + title: PropTypes.string, + onDrop: PropTypes.func.isRequired, + dropMessage: PropTypes.string, + accept: PropTypes.object, + maxFiles: PropTypes.number, +} + +export default CippDropzone diff --git a/src/importsMap.jsx b/src/importsMap.jsx index 21174e32a0f2..f7c2a6e83234 100644 --- a/src/importsMap.jsx +++ b/src/importsMap.jsx @@ -115,6 +115,7 @@ import React from 'react' "/email/tools/mailbox-restore-wizard": React.lazy(() => import('./views/email-exchange/tools/MailboxRestoreWizard')), "/email/tools/mailbox-restores": React.lazy(() => import('./views/email-exchange/tools/MailboxRestores')), "/email/tools/mail-test": React.lazy(() => import('./views/email-exchange/tools/MailTest')), + "/email/tools/message-viewer": React.lazy(() => import('./views/email-exchange/tools/MessageViewer')), "/email/spamfilter/add-template": React.lazy(() => import('./views/email-exchange/spamfilter/AddSpamfilterTemplate')), "/email/administration/edit-mailbox-permissions": React.lazy(() => import('./views/email-exchange/administration/EditMailboxPermissions')), "/email/administration/add-shared-mailbox": React.lazy(() => import('./views/email-exchange/administration/AddSharedMailbox')), diff --git a/src/routes.json b/src/routes.json index c0a996610e41..be956782af6d 100644 --- a/src/routes.json +++ b/src/routes.json @@ -776,6 +776,12 @@ "component": "views/email-exchange/tools/MailTest", "allowedRoles": ["admin", "editor", "readonly"] }, + { + "path": "/email/tools/message-viewer", + "name": "Message Viewer", + "component": "views/email-exchange/tools/MessageViewer", + "allowedRoles": ["admin", "editor", "readonly"] + }, { "path": "/email/spamfilter/add-template", "name": "Add Spamfilter Template", diff --git a/src/views/email-exchange/tools/MessageViewer.jsx b/src/views/email-exchange/tools/MessageViewer.jsx new file mode 100644 index 000000000000..314762758830 --- /dev/null +++ b/src/views/email-exchange/tools/MessageViewer.jsx @@ -0,0 +1,310 @@ +import React, { useCallback, useEffect, useMemo, useState } from 'react' +import PropTypes from 'prop-types' +import { CippPage, CippMasonry, CippMasonryItem, CippContentCard } from 'src/components/layout' +import { parseEml, readEml, GBKUTF8, decode } from 'eml-parse-js' +import { useMediaPredicate } from 'react-media-hook' +import { useSelector } from 'react-redux' +import { CellDate } from 'src/components/tables' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { + CButton, + CCard, + CCardBody, + CCol, + CDropdown, + CDropdownMenu, + CDropdownToggle, + CLink, + CRow, +} from '@coreui/react' +import ReactTimeAgo from 'react-time-ago' +import { CippCodeBlock, ModalService } from 'src/components/utilities' +import DOMPurify from 'dompurify' +import ReactHtmlParser from 'react-html-parser' +import CippDropzone from 'src/components/utilities/CippDropzone' + +const MessageViewer = ({ emlFile }) => { + const [emlContent, setEmlContent] = useState(null) + const [emailSource, setEmailSource] = useState(emlFile) + const [emlError, setEmlError] = useState(false) + const [messageHtml, setMessageHtml] = useState('') + + const getAttachmentIcon = (contentType) => { + if (contentType.includes('image')) { + return 'image' + } else if (contentType.includes('audio')) { + return 'volume-up' + } else if (contentType.includes('video')) { + return 'video' + } else if (contentType.includes('text')) { + return 'file-lines' + } else if (contentType.includes('pdf')) { + return 'file-pdf' + } else if ( + contentType.includes('zip') || + contentType.includes('compressed') || + contentType.includes('tar') || + contentType.includes('gzip') + ) { + return 'file-zipper' + } else if (contentType.includes('msword')) { + return 'file-word' + } else if (contentType.includes('spreadsheet')) { + return 'file-excel' + } else if (contentType.includes('presentation')) { + return 'file-powerpoint' + } else if (contentType.includes('json') || contentType.includes('xml')) { + return 'file-code' + } else if (contentType.includes('rfc822')) { + return 'envelope' + } else { + return 'file' + } + } + + const downloadAttachment = (attachment, newTab = false) => { + if (attachment?.data) { + var contentType = attachment?.contentType?.split(';')[0] ?? 'text/plain' + var fileBytes = attachment.data + var fileName = attachment.name + downloadFileBytes(fileName, fileBytes, contentType, newTab) + } else { + downloadFile(attachment.name, attachment.data64) + } + } + + const downloadFile = (fileName, base64Content, newTab) => { + const link = document.createElement('a') + link.href = `data:application/octet-stream;base64,${base64Content}` + link.download = fileName + link.click() + } + + const downloadFileBytes = (fileName, fileBytes, contentType, newTab = false) => { + const blob = new Blob([fileBytes], { type: contentType ?? 'application/octet-stream' }) + const url = URL.createObjectURL(blob) + const link = document.createElement('a') + if (newTab) { + if (contentType.includes('rfc822')) { + var content = fileBytes + const nestedMessage = + ModalService.open({ + body: nestedMessage, + title: fileName, + size: 'lg', + }) + } else { + const newWindow = window.open() + newWindow.location.href = url + } + } else { + link.href = url + link.download = fileName + link.click() + URL.revokeObjectURL(url) + } + } + + function isValidDate(d) { + return d instanceof Date && !isNaN(d) + } + + const showEmailModal = (emailSource) => { + ModalService.open({ + data: emailSource, + componentType: 'codeblock', + title: 'Email Source', + size: 'lg', + }) + } + + const EmailButtons = (emailSource) => { + return ( + showEmailModal(emailSource)}> + + View Source + + ) + } + + useEffect(() => { + readEml(emailSource, (err, ReadEmlJson) => { + if (err) { + setEmlError(true) + setEmlContent(null) + setMessageHtml(null) + } else { + setEmlContent(ReadEmlJson) + setEmlError(false) + if (ReadEmlJson.html) { + var sanitizedHtml = DOMPurify.sanitize(ReadEmlJson.html) + var parsedHtml = ReactHtmlParser(sanitizedHtml) + setMessageHtml(parsedHtml) + } else { + setMessageHtml(null) + } + } + }) + }, [emailSource, setMessageHtml, setEmailSource, setEmlError, setEmlContent]) + + var buttons = EmailButtons(emailSource) + + return ( + <> + {emlError && ( + + Unable to parse the EML file, email source is displayed below. + + + )} + + {emlContent && ( + <> + + <> + + +
+ + {emlContent?.from?.name} <{emlContent?.from?.email}> +
+ {emlContent?.to?.length > 0 && ( +
+ + To:{' '} + {emlContent?.to?.map((to) => to.name + ' <' + to.email + '>').join(', ')} + +
+ )} + {emlContent?.cc?.length > 0 && ( +
+ + CC:{' '} + {emlContent?.cc?.map((cc) => cc.name + ' <' + cc.email + '>').join(', ')} + +
+ )} +
+ +
+ + + {emlContent.date && isValidDate(emlContent.date) + ? emlContent.date.toLocaleDateString() + : 'Invalid Date'} + + {emlContent.date && isValidDate(emlContent.date) && ( + <> + () + + )} + +
+
+
+ + + {emlContent.attachments && emlContent.attachments.length > 0 && ( + + + {emlContent.attachments.map((attachment, index) => ( + + + + {attachment.name ?? 'No name'} + + + downloadAttachment(attachment)} + > + + Download + + {(attachment?.contentType === undefined || + attachment?.contentType?.includes('text') || + attachment?.contentType?.includes('pdf') || + attachment?.contentType?.includes('image') || + attachment?.contentType?.includes('rfc822')) && ( + downloadAttachment(attachment, true)} + > + + View + + )} + + + ))} + + + )} + + {(emlContent?.text || emlContent?.html) && ( + + + {messageHtml ? ( +
{messageHtml}
+ ) : ( +
+ +
+ )} +
+
+ )} +
+ + )} + + ) +} + +MessageViewer.propTypes = { + emlFile: PropTypes.string, +} + +const MessageViewerPage = () => { + const [emlFile, setEmlFile] = useState(null) + const onDrop = useCallback((acceptedFiles) => { + acceptedFiles.forEach((file) => { + const reader = new FileReader() + + reader.onabort = () => console.log('file reading was aborted') + reader.onerror = () => console.log('file reading has failed') + reader.onload = () => { + setEmlFile(reader.result) + } + reader.readAsText(file) + }) + }, []) + + return ( + + + {emlFile && } + + ) +} + +export default MessageViewerPage From 9b51796b206dde86252543a00c4bebaf690d81b1 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Wed, 31 Jul 2024 23:33:12 +0200 Subject: [PATCH 38/52] upgrade user schedulder experience. --- src/views/identity/administration/AddUser.jsx | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/views/identity/administration/AddUser.jsx b/src/views/identity/administration/AddUser.jsx index 96186c358ed9..ad772b208d1a 100644 --- a/src/views/identity/administration/AddUser.jsx +++ b/src/views/identity/administration/AddUser.jsx @@ -37,8 +37,12 @@ import useQuery from 'src/hooks/useQuery' import Select from 'react-select' import { useNavigate } from 'react-router-dom' import { OnChange } from 'react-final-form-listeners' +import DatePicker from 'react-datepicker' +import 'react-datepicker/dist/react-datepicker.css' const AddUser = () => { + const currentDate = new Date() + const [startDate, setStartDate] = useState(currentDate) let navigate = useNavigate() const [addedAttributes, setAddedAttribute] = React.useState(0) const tenant = useSelector((state) => state.app.currentTenant) @@ -81,6 +85,8 @@ const AddUser = () => { values.addedAttributes.push({ Key: key, Value: values.defaultAttributes[key].Value }) }) } + const unixTime = Math.floor(startDate.getTime() / 1000) + const shippedValues = { AddedAliases: values.addedAliases ? values.addedAliases : '', BusinessPhone: values.businessPhones, @@ -106,6 +112,10 @@ const AddUser = () => { tenantID: tenantDomain, addedAttributes: values.addedAttributes, setManager: values.setManager, + Scheduled: values.Scheduled?.enabled ? { enabled: true, date: unixTime } : { enabled: false }, + PostExecution: values.Scheduled?.enabled + ? { webhook: values.webhook, psa: values.psa, email: values.email } + : '', ...values.license, } //window.alert(JSON.stringify(shippedValues)) @@ -408,6 +418,33 @@ const AddUser = () => { /> {usersError && Failed to load list of users} + + + + + + + + + setStartDate(date)} + /> + + + + + + + + + + From 3f20d802064b8322a32aa9fad0dfe129c321714a Mon Sep 17 00:00:00 2001 From: John Duprey Date: Wed, 31 Jul 2024 19:42:03 -0400 Subject: [PATCH 39/52] message view bugfixes --- .../email-exchange/tools/MessageViewer.jsx | 66 ++++++++++++------- 1 file changed, 41 insertions(+), 25 deletions(-) diff --git a/src/views/email-exchange/tools/MessageViewer.jsx b/src/views/email-exchange/tools/MessageViewer.jsx index 314762758830..52d7c3b637a4 100644 --- a/src/views/email-exchange/tools/MessageViewer.jsx +++ b/src/views/email-exchange/tools/MessageViewer.jsx @@ -23,9 +23,8 @@ import DOMPurify from 'dompurify' import ReactHtmlParser from 'react-html-parser' import CippDropzone from 'src/components/utilities/CippDropzone' -const MessageViewer = ({ emlFile }) => { +const MessageViewer = ({ emailSource }) => { const [emlContent, setEmlContent] = useState(null) - const [emailSource, setEmailSource] = useState(emlFile) const [emlError, setEmlError] = useState(false) const [messageHtml, setMessageHtml] = useState('') @@ -63,39 +62,57 @@ const MessageViewer = ({ emlFile }) => { } const downloadAttachment = (attachment, newTab = false) => { - if (attachment?.data) { - var contentType = attachment?.contentType?.split(';')[0] ?? 'text/plain' - var fileBytes = attachment.data - var fileName = attachment.name - downloadFileBytes(fileName, fileBytes, contentType, newTab) - } else { - downloadFile(attachment.name, attachment.data64) + var contentType = attachment?.contentType?.split(';')[0] ?? 'text/plain' + var fileBytes = attachment.data + if (fileBytes instanceof Uint8Array && attachment?.data64) { + fileBytes = new Uint8Array( + atob(attachment.data64) + .split('') + .map((c) => c.charCodeAt(0)), + ) } - } - - const downloadFile = (fileName, base64Content, newTab) => { - const link = document.createElement('a') - link.href = `data:application/octet-stream;base64,${base64Content}` - link.download = fileName - link.click() - } - - const downloadFileBytes = (fileName, fileBytes, contentType, newTab = false) => { + var fileName = attachment.name const blob = new Blob([fileBytes], { type: contentType ?? 'application/octet-stream' }) const url = URL.createObjectURL(blob) const link = document.createElement('a') if (newTab) { if (contentType.includes('rfc822')) { var content = fileBytes - const nestedMessage = + const nestedMessage = ModalService.open({ body: nestedMessage, title: fileName, size: 'lg', }) + } else if (contentType.includes('pdf')) { + const embeddedPdf = + ModalService.open({ + body: embeddedPdf, + title: fileName, + size: 'lg', + }) + } else if (contentType.includes('image')) { + const embeddedImage = {fileName} + ModalService.open({ + body: embeddedImage, + title: fileName, + size: 'lg', + }) + } else if (contentType.includes('text')) { + const textContent = fileBytes + ModalService.open({ + data: textContent, + componentType: 'codeblock', + title: fileName, + size: 'lg', + }) + setTimeout(() => { + URL.revokeObjectURL(url) + }, 1000) } else { const newWindow = window.open() newWindow.location.href = url + URL.revokeObjectURL(url) } } else { link.href = url @@ -145,7 +162,7 @@ const MessageViewer = ({ emlFile }) => { } } }) - }, [emailSource, setMessageHtml, setEmailSource, setEmlError, setEmlContent]) + }, [emailSource, setMessageHtml, setEmlError, setEmlContent]) var buttons = EmailButtons(emailSource) @@ -238,7 +255,7 @@ const MessageViewer = ({ emlFile }) => { className="dropdown-item" onClick={() => downloadAttachment(attachment, true)} > - + View )} @@ -274,7 +291,7 @@ const MessageViewer = ({ emlFile }) => { } MessageViewer.propTypes = { - emlFile: PropTypes.string, + emailSource: PropTypes.string, } const MessageViewerPage = () => { @@ -282,7 +299,6 @@ const MessageViewerPage = () => { const onDrop = useCallback((acceptedFiles) => { acceptedFiles.forEach((file) => { const reader = new FileReader() - reader.onabort = () => console.log('file reading was aborted') reader.onerror = () => console.log('file reading has failed') reader.onload = () => { @@ -302,7 +318,7 @@ const MessageViewerPage = () => { dropMessage="Drag an EML file or click to add" maxFiles={1} /> - {emlFile && } + {emlFile && } ) } From cff54a8fbd26c98f154a9cade3af684cba243d10 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Wed, 31 Jul 2024 20:00:02 -0400 Subject: [PATCH 40/52] add blob: to content-security-policy --- staticwebapp.config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/staticwebapp.config.json b/staticwebapp.config.json index 8a0fafca07d4..74988468595b 100644 --- a/staticwebapp.config.json +++ b/staticwebapp.config.json @@ -103,7 +103,7 @@ } }, "globalHeaders": { - "content-security-policy": "default-src https: 'unsafe-eval' 'unsafe-inline'; object-src 'none'; img-src 'self' data: *" + "content-security-policy": "default-src https: 'unsafe-eval' 'unsafe-inline'; object-src 'self' blob:; img-src 'self' blob: data: *" }, "mimeTypes": { ".json": "text/json" From ff56eef918f8e04122c6983bc67c51680185845a Mon Sep 17 00:00:00 2001 From: John Duprey Date: Wed, 31 Jul 2024 20:05:33 -0400 Subject: [PATCH 41/52] add blob to default-src --- staticwebapp.config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/staticwebapp.config.json b/staticwebapp.config.json index 74988468595b..0c36ddcac257 100644 --- a/staticwebapp.config.json +++ b/staticwebapp.config.json @@ -103,7 +103,7 @@ } }, "globalHeaders": { - "content-security-policy": "default-src https: 'unsafe-eval' 'unsafe-inline'; object-src 'self' blob:; img-src 'self' blob: data: *" + "content-security-policy": "default-src https: blob: 'unsafe-eval' 'unsafe-inline'; object-src 'self' blob:; img-src 'self' blob: data: *" }, "mimeTypes": { ".json": "text/json" From f978031b21ed9b612e57cbeb32b47ea122e28b44 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Wed, 31 Jul 2024 20:19:16 -0400 Subject: [PATCH 42/52] sanitize secure score html --- .../tenant/administration/SecureScore.jsx | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/views/tenant/administration/SecureScore.jsx b/src/views/tenant/administration/SecureScore.jsx index be036997339c..bfa9d2e751c2 100644 --- a/src/views/tenant/administration/SecureScore.jsx +++ b/src/views/tenant/administration/SecureScore.jsx @@ -28,6 +28,8 @@ import { CellTip, cellGenericFormatter } from 'src/components/tables/CellGeneric import { CippCallout } from 'src/components/layout' import CippPrettyCard from 'src/components/contentcards/CippPrettyCard' import { TableModalButton } from 'src/components/buttons' +import DOMPurify from 'dompurify' +import ReactHtmlParser from 'react-html-parser' const SecureScore = () => { const textRef = useRef() @@ -66,6 +68,12 @@ const SecureScore = () => { }, }) + const sanitizeHtml = (html) => { + var sanitizedHtml = DOMPurify.sanitize(html) + var parsedHtml = ReactHtmlParser(sanitizedHtml) + return parsedHtml + } + useEffect(() => { if (isSuccess) { setTranslatedData(securescore.Results[0]) @@ -341,23 +349,16 @@ const SecureScore = () => {
Description
-
+
+ {sanitizeHtml(`${info.description} ${info.implementationStatus}`)} +
{info.scoreInPercentage !== 100 && (
Remediation Recommendation
- { -
- } + {
{sanitizeHtml(info.remediation)}
}
)} From e4e5ab15c86afa76f2e77028728e1bb599012c6b Mon Sep 17 00:00:00 2001 From: John Duprey Date: Wed, 31 Jul 2024 20:44:54 -0400 Subject: [PATCH 43/52] Fix revokesession bulk action --- src/views/identity/administration/Users.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/identity/administration/Users.jsx b/src/views/identity/administration/Users.jsx index 3bd663d7e063..669e401a3611 100644 --- a/src/views/identity/administration/Users.jsx +++ b/src/views/identity/administration/Users.jsx @@ -589,7 +589,7 @@ const Users = (row) => { label: 'Revoke sessions', color: 'info', modal: true, - modalUrl: `/api/ExecRevokeSessions?Enable=true&TenantFilter=!Tenant&ID=!userPrincipalName`, + modalUrl: `/api/ExecRevokeSessions?Enable=true&TenantFilter=!Tenant&ID=!id&Username=!userPrincipalName`, modalMessage: 'Are you sure you want to revoke all sessions for these users?', }, { From 8be3b6d85b95ef903ad1522d667483b4c845cbed Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 1 Aug 2024 01:11:50 -0400 Subject: [PATCH 44/52] SAM Roles --- .../cipp/app-settings/SettingsSuperAdmin.jsx | 38 +---- .../components/SettingsSAMRoles.jsx | 133 ++++++++++++++++++ 2 files changed, 135 insertions(+), 36 deletions(-) create mode 100644 src/views/cipp/app-settings/components/SettingsSAMRoles.jsx diff --git a/src/views/cipp/app-settings/SettingsSuperAdmin.jsx b/src/views/cipp/app-settings/SettingsSuperAdmin.jsx index 4e38038fb68c..18cb6b397980 100644 --- a/src/views/cipp/app-settings/SettingsSuperAdmin.jsx +++ b/src/views/cipp/app-settings/SettingsSuperAdmin.jsx @@ -7,6 +7,7 @@ import { CippCallout } from 'src/components/layout/index.js' import CippAccordionItem from 'src/components/contentcards/CippAccordionItem' import SettingsCustomRoles from 'src/views/cipp/app-settings/components/SettingsCustomRoles' import CippButtonCard from 'src/components/contentcards/CippButtonCard' +import SettingsSAMRoles from './components/SettingsSAMRoles' export function SettingsSuperAdmin() { const partnerConfig = useGenericGetRequestQuery({ @@ -65,46 +66,11 @@ export function SettingsSuperAdmin() {

- - -

Tenant Mode

-
( - <> - {partnerConfig.isFetching && } - - - - - - - )} - /> - {webhookCreateResult.isSuccess && ( - - {webhookCreateResult?.data?.results} - - )} - - + ) } diff --git a/src/views/cipp/app-settings/components/SettingsSAMRoles.jsx b/src/views/cipp/app-settings/components/SettingsSAMRoles.jsx new file mode 100644 index 000000000000..cb687914def3 --- /dev/null +++ b/src/views/cipp/app-settings/components/SettingsSAMRoles.jsx @@ -0,0 +1,133 @@ +import React, { useRef, useState } from 'react' +import { + CButton, + CCallout, + CCol, + CForm, + CRow, + CAccordion, + CAccordionHeader, + CAccordionBody, + CAccordionItem, +} from '@coreui/react' +import { Field, Form, FormSpy } from 'react-final-form' +import { RFFCFormRadioList, RFFSelectSearch } from 'src/components/forms' +import { useGenericGetRequestQuery, useLazyGenericPostRequestQuery } from 'src/store/api/app' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { TenantSelectorMultiple, ModalService, CippOffcanvas } from 'src/components/utilities' +import PropTypes from 'prop-types' +import { OnChange } from 'react-final-form-listeners' +import { useListTenantsQuery } from 'src/store/api/tenants' +import { OffcanvasListSection } from 'src/components/utilities/CippListOffcanvas' +import CippButtonCard from 'src/components/contentcards/CippButtonCard' +import GDAPRoles from 'src/data/GDAPRoles' + +const SettingsSAMRoles = () => { + const [genericPostRequest, postResults] = useLazyGenericPostRequestQuery() + const [selectedTenant, setSelectedTenant] = useState([]) + const tenantSelectorRef = useRef() + const { data: tenants = [], tenantsFetching } = useListTenantsQuery({ + showAllTenantSelector: true, + }) + + const { + data: cippSAMRoles = [], + isFetching: roleListFetching, + isSuccess: roleListSuccess, + refetch: refetchRoleList, + } = useGenericGetRequestQuery({ + path: 'api/ExecSAMRoles', + }) + + const handleTenantChange = (e) => { + setSelectedTenant(e) + } + + const handleSubmit = async (values) => { + //filter on only objects that are 'true' + genericPostRequest({ + path: '/api/ExecSAMRoles?Action=Update', + values: { + Roles: values.Roles, + Tenants: selectedTenant.map((tenant) => tenant.value), + }, + }).then(() => { + refetchRoleList() + }) + } + + return ( + + <> +

+ Add your CIPP-SAM application Service Principal directly to Admin Roles in the tenant. + This is an advanced use case where you need access to additional Graph endpoints or + Exchange Cmdlets otherwise unavailable via Delegated permissions. +

+

+ This functionality is in + beta and should be treated as such. Roles are added during the Update Permissions process + or a CPV refresh. +

+ + { + return ( + + + +
+ ({ + name: role.Name, + value: role.ObjectId, + }))} + isLoading={roleListFetching} + multi={true} + refreshFunction={() => refetchRoleList()} + placeholder="Select admin roles" + /> +
+
+
Selected Tenants
+ handleTenantChange(e)} + /> +
+
+
+ + {postResults.isSuccess && ( + {postResults.data.Results} + )} + + + + + Save + + + + +
+ ) + }} + /> + +
+ ) +} + +export default SettingsSAMRoles From 8ac22e9abecb8a95a922bf88d0586dc21dc97509 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Thu, 1 Aug 2024 12:06:34 +0200 Subject: [PATCH 45/52] add device compliance alert --- src/data/alerts.json | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/data/alerts.json b/src/data/alerts.json index 2d635fb529f9..835216740c4d 100644 --- a/src/data/alerts.json +++ b/src/data/alerts.json @@ -94,5 +94,10 @@ "name": "SoftDeletedMailboxes", "label": "Alert on soft deleted mailboxes", "recommendedRunInterval": "1d" + }, + { + "name": "DeviceCompliance", + "label": "Alert on device compliance issues", + "recommendedRunInterval": "4h" } -] \ No newline at end of file +] From 2cb908a8d2aed95491cd7aad18773186d2da3ddc Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Thu, 1 Aug 2024 12:49:37 +0200 Subject: [PATCH 46/52] fixes https://github.com/KelvinTegelaar/CIPP/issues/2710 --- src/views/identity/administration/RiskyUsers.jsx | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/views/identity/administration/RiskyUsers.jsx b/src/views/identity/administration/RiskyUsers.jsx index d51a899bef85..0b8ae07e7e8d 100644 --- a/src/views/identity/administration/RiskyUsers.jsx +++ b/src/views/identity/administration/RiskyUsers.jsx @@ -88,6 +88,20 @@ const RiskyUsers = () => { } const columns = [ + { + name: 'Tenant', + selector: (row) => row['Tenant'], + sortable: true, + exportSelector: 'Tenant', + omit: tenant.defaultDomainName === 'allTenants' ? false : true, + }, + { + name: 'Status', + selector: (row) => row['CippStatus'], + sortable: true, + exportSelector: 'CippStatus', + omit: tenant.defaultDomainName === 'allTenants' ? false : true, + }, { name: 'Risk Last Updated Date', selector: (row) => row['riskLastUpdatedDateTime'], From f79e0c819db99857856e73a35ca2bdc0649326e4 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Thu, 1 Aug 2024 12:52:38 +0200 Subject: [PATCH 47/52] fixes https://github.com/KelvinTegelaar/CIPP/issues/2710 --- src/views/identity/administration/RiskyUsers.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/views/identity/administration/RiskyUsers.jsx b/src/views/identity/administration/RiskyUsers.jsx index 0b8ae07e7e8d..9d0b949b135a 100644 --- a/src/views/identity/administration/RiskyUsers.jsx +++ b/src/views/identity/administration/RiskyUsers.jsx @@ -93,14 +93,14 @@ const RiskyUsers = () => { selector: (row) => row['Tenant'], sortable: true, exportSelector: 'Tenant', - omit: tenant.defaultDomainName === 'allTenants' ? false : true, + omit: tenant.defaultDomainName === 'AllTenants' ? false : true, }, { name: 'Status', selector: (row) => row['CippStatus'], sortable: true, exportSelector: 'CippStatus', - omit: tenant.defaultDomainName === 'allTenants' ? false : true, + omit: tenant.defaultDomainName === 'AllTenants' ? false : true, }, { name: 'Risk Last Updated Date', From 06ba66bea0963ab7996d7dedbda428968daa9005 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 1 Aug 2024 08:35:25 -0400 Subject: [PATCH 48/52] Fix tenant selector --- .../components/SettingsSAMRoles.jsx | 125 ++++++++++-------- 1 file changed, 71 insertions(+), 54 deletions(-) diff --git a/src/views/cipp/app-settings/components/SettingsSAMRoles.jsx b/src/views/cipp/app-settings/components/SettingsSAMRoles.jsx index cb687914def3..ebc8310b6abd 100644 --- a/src/views/cipp/app-settings/components/SettingsSAMRoles.jsx +++ b/src/views/cipp/app-settings/components/SettingsSAMRoles.jsx @@ -1,4 +1,4 @@ -import React, { useRef, useState } from 'react' +import React, { useEffect, useRef, useState } from 'react' import { CButton, CCallout, @@ -26,7 +26,11 @@ const SettingsSAMRoles = () => { const [genericPostRequest, postResults] = useLazyGenericPostRequestQuery() const [selectedTenant, setSelectedTenant] = useState([]) const tenantSelectorRef = useRef() - const { data: tenants = [], tenantsFetching } = useListTenantsQuery({ + const { + data: tenants = [], + isFetching: tenantsFetching, + isSuccess: tenantSuccess, + } = useListTenantsQuery({ showAllTenantSelector: true, }) @@ -56,8 +60,20 @@ const SettingsSAMRoles = () => { }) } + useEffect(() => { + if (roleListSuccess && cippSAMRoles.Tenants.length > 0) { + var selectedTenants = [] + tenants.map((tenant) => { + if (cippSAMRoles.Tenants.includes(tenant.customerId)) { + selectedTenants.push({ label: tenant.displayName, value: tenant.customerId }) + } + }) + tenantSelectorRef.current.setValue(selectedTenants) + } + }, [cippSAMRoles, roleListSuccess, tenantSuccess, tenantSelectorRef, tenants]) + return ( - + <>

Add your CIPP-SAM application Service Principal directly to Admin Roles in the tenant. @@ -70,61 +86,62 @@ const SettingsSAMRoles = () => { or a CPV refresh.

- { - return ( - - - -
- ({ - name: role.Name, - value: role.ObjectId, - }))} - isLoading={roleListFetching} - multi={true} - refreshFunction={() => refetchRoleList()} - placeholder="Select admin roles" - /> -
-
-
Selected Tenants
- handleTenantChange(e)} - /> -
-
-
- - {postResults.isSuccess && ( - {postResults.data.Results} - )} + {roleListSuccess && ( + { + return ( + - - - +
+ ({ + name: role.Name, + value: role.ObjectId, + }))} + multi={true} + refreshFunction={() => refetchRoleList()} + placeholder="Select admin roles" /> - Save - +
+
+
Selected Tenants
+ handleTenantChange(e)} + /> +
-
-
- ) - }} - /> + + {postResults.isSuccess && ( + {postResults.data.Results} + )} + + + + + Save + + + + + + ) + }} + /> + )}
) From 47e8e7f59feb14bdc2439a85b948df943317fbc1 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Thu, 1 Aug 2024 15:29:53 +0200 Subject: [PATCH 49/52] Add edit named locations --- .../tenant/conditional/NamedLocations.jsx | 87 +++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/src/views/tenant/conditional/NamedLocations.jsx b/src/views/tenant/conditional/NamedLocations.jsx index b7f1ebb0f68c..817528a2078c 100644 --- a/src/views/tenant/conditional/NamedLocations.jsx +++ b/src/views/tenant/conditional/NamedLocations.jsx @@ -22,6 +22,88 @@ function DateNotNull(date) { return date.toString().trim() + 'Z' } +const Offcanvas = (row, rowIndex, formatExtraData) => { + const tenant = useSelector((state) => state.app.currentTenant) + const [ocVisible, setOCVisible] = useState(false) + return ( + <> + setOCVisible(true)}> + + + setOCVisible(false)} + /> + + ) +} const columns = [ { name: 'Name', @@ -62,6 +144,11 @@ const columns = [ exportSelector: 'modifiedDateTime', maxWidth: '150px', }, + { + name: 'Actions', + cell: Offcanvas, + maxWidth: '80px', + }, ] const NamedLocationsList = () => { From c70d679fe0570187774a9a98fb5a912feefd5ebc Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Thu, 1 Aug 2024 15:43:52 +0200 Subject: [PATCH 50/52] test --- src/views/cipp/ExtensionMappings.jsx | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/views/cipp/ExtensionMappings.jsx b/src/views/cipp/ExtensionMappings.jsx index 4de976555ccc..816a679e9905 100644 --- a/src/views/cipp/ExtensionMappings.jsx +++ b/src/views/cipp/ExtensionMappings.jsx @@ -218,11 +218,7 @@ export default function ExtensionMappings({ type, fieldMappings = false, autoMap { - return !Object.keys(listMappingBackendResult.data?.Mappings).includes( - tenant.customerId, - ) - }).map((tenant) => ({ + values={listMappingBackendResult.data?.Tenants.map((tenant) => ({ name: tenant.displayName, value: tenant.customerId, }))} From 930f99700d2b3d1c3c20c800d202d3ece43f458c Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Thu, 1 Aug 2024 15:48:47 +0200 Subject: [PATCH 51/52] test fix for extension multi mappings --- src/views/cipp/ExtensionMappings.jsx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/views/cipp/ExtensionMappings.jsx b/src/views/cipp/ExtensionMappings.jsx index 816a679e9905..5bcf9e23e8e8 100644 --- a/src/views/cipp/ExtensionMappings.jsx +++ b/src/views/cipp/ExtensionMappings.jsx @@ -218,7 +218,11 @@ export default function ExtensionMappings({ type, fieldMappings = false, autoMap ({ + values={listMappingBackendResult.data?.Tenants.filter((tenant) => { + return !Object.keys(listMappingBackendResult.data?.Mappings).includes( + tenant.customerId, + ) + }).map((tenant) => ({ name: tenant.displayName, value: tenant.customerId, }))} @@ -247,9 +251,7 @@ export default function ExtensionMappings({ type, fieldMappings = false, autoMap if ( mappingValue.value !== undefined && mappingValue.value !== '-1' && - Object.values(mappingArray) - .map((item) => item.companyId) - .includes(mappingValue.value) === false + Object.values(mappingArray).map((item) => item.companyId) ) { setMappingArray([ ...mappingArray, From 1d94b1e20fa67bc9e2f54de62462cb2c61c65360 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Thu, 1 Aug 2024 15:57:45 +0200 Subject: [PATCH 52/52] fallback to actual id --- src/components/tables/CellLicense.jsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/tables/CellLicense.jsx b/src/components/tables/CellLicense.jsx index 50b88b978c4f..ff5df5329743 100644 --- a/src/components/tables/CellLicense.jsx +++ b/src/components/tables/CellLicense.jsx @@ -8,6 +8,8 @@ export function CellLicense({ cell }) { if (licenseAssignment.skuId == M365Licenses[x].GUID) { licenses.push(M365Licenses[x].Product_Display_Name) break + } else { + licenses.push(licenseAssignment.skuId) } } })