From 8c7296f3d0c452cf4698cbd754983df37752449d Mon Sep 17 00:00:00 2001
From: Esco
Date: Sat, 27 Jul 2024 16:54:20 +0200
Subject: [PATCH 01/37] 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 02/37] 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 03/37] 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 04/37] 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 05/37] 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 06/37] 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 07/37] 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 08/37] 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 09/37] 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 10/37] 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 11/37] 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 12/37] 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 13/37] 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 }
+
+
+
+
+
+
+
+ Scheduled creation Date
+ setStartDate(date)}
+ />
+
+
+
+ Send results to
+
+
+
+
+
+
From 3f20d802064b8322a32aa9fad0dfe129c321714a Mon Sep 17 00:00:00 2001
From: John Duprey
Date: Wed, 31 Jul 2024 19:42:03 -0400
Subject: [PATCH 14/37] 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 =
+ 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 15/37] 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 16/37] 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 17/37] 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 18/37] 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 19/37] 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
-
-
>
>
+
>
)
}
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.
+
+
+
+ )
+}
+
+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 20/37] 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 21/37] 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 22/37] 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 23/37] 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.
-
)
From 47e8e7f59feb14bdc2439a85b948df943317fbc1 Mon Sep 17 00:00:00 2001
From: KelvinTegelaar
Date: Thu, 1 Aug 2024 15:29:53 +0200
Subject: [PATCH 24/37] 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 25/37] 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 26/37] 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 27/37] 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)
}
}
})
From ed1e05be4004d18af54533032feb522e000532b0 Mon Sep 17 00:00:00 2001
From: John Duprey
Date: Thu, 1 Aug 2024 14:14:25 -0400
Subject: [PATCH 28/37] Audit logs
---
src/_nav.jsx | 9 +-
src/importsMap.jsx | 3 +-
src/routes.json | 10 +-
.../tenant/administration/ListAlertsQueue.jsx | 2 +-
.../tenant/administration/ListAuditLogs.jsx | 233 ++++++++++++++++++
5 files changed, 251 insertions(+), 6 deletions(-)
create mode 100644 src/views/tenant/administration/ListAuditLogs.jsx
diff --git a/src/_nav.jsx b/src/_nav.jsx
index 59d2980e2ae1..bc1505aa763e 100644
--- a/src/_nav.jsx
+++ b/src/_nav.jsx
@@ -145,8 +145,13 @@ const _nav = [
},
{
component: CNavItem,
- name: 'Alerts',
- to: '/tenant/administration/alertsqueue',
+ name: 'Alert Configuration',
+ to: '/tenant/administration/alert-configuration',
+ },
+ {
+ component: CNavItem,
+ name: 'Audit Logs',
+ to: '/tenant/administration/audit-logs',
},
{
component: CNavItem,
diff --git a/src/importsMap.jsx b/src/importsMap.jsx
index f7c2a6e83234..3708a44f4ed9 100644
--- a/src/importsMap.jsx
+++ b/src/importsMap.jsx
@@ -42,7 +42,8 @@ import React from 'react'
"/tenant/administration/domains": React.lazy(() => import('./views/tenant/administration/Domains')),
"/tenant/administration/alertswizard": React.lazy(() => import('./views/tenant/administration/AlertWizard')),
"/tenant/administration/alertrules": React.lazy(() => import('./views/tenant/administration/AlertRules')),
- "/tenant/administration/alertsqueue": React.lazy(() => import('./views/tenant/administration/ListAlertsQueue')),
+ "/tenant/administration/alert-configuration": React.lazy(() => import('./views/tenant/administration/ListAlertsQueue')),
+ "/tenant/administration/audit-logs": React.lazy(() => import('./views/tenant/administration/ListAuditLogs')),
"/tenant/administration/graph-explorer": React.lazy(() => import('./views/tenant/administration/GraphExplorer')),
"/tenant/administration/service-health": React.lazy(() => import('./views/tenant/administration/ServiceHealth')),
"/tenant/administration/enterprise-apps": React.lazy(() => import('./views/tenant/administration/ListEnterpriseApps')),
diff --git a/src/routes.json b/src/routes.json
index be956782af6d..73f0383bbd20 100644
--- a/src/routes.json
+++ b/src/routes.json
@@ -279,11 +279,17 @@
"allowedRoles": ["admin", "editor", "readonly"]
},
{
- "path": "/tenant/administration/alertsqueue",
- "name": "Alerts Queue",
+ "path": "/tenant/administration/alert-configuration",
+ "name": "Alert Configuration",
"component": "views/tenant/administration/ListAlertsQueue",
"allowedRoles": ["admin", "editor", "readonly"]
},
+ {
+ "path": "/tenant/administration/audit-logs",
+ "name": "Audit Logs",
+ "component": "views/tenant/administration/ListAuditLogs",
+ "allowedRoles": ["admin", "editor", "readonly"]
+ },
{
"path": "/tenant/administration/graph-explorer",
"name": "Graph Explorer",
diff --git a/src/views/tenant/administration/ListAlertsQueue.jsx b/src/views/tenant/administration/ListAlertsQueue.jsx
index 7efe849c13a4..7a715188a824 100644
--- a/src/views/tenant/administration/ListAlertsQueue.jsx
+++ b/src/views/tenant/administration/ListAlertsQueue.jsx
@@ -65,7 +65,7 @@ const ListClassicAlerts = () => {
allTenants: true,
helpContext: 'https://google.com',
}}
- title="Alerts List"
+ title="Alert Configuration"
titleButton={
{
+ const [interval, setInterval] = React.useState('d')
+ const [time, setTime] = React.useState(1)
+ const [relativeTime, setRelativeTime] = React.useState('1d')
+ const [startDate, setStartDate] = React.useState(null)
+ const [endDate, setEndDate] = React.useState(null)
+ const [visibleA, setVisibleA] = React.useState(true)
+ const [tenantColumnSet, setTenantColumn] = React.useState(false)
+ const tenant = useSelector((state) => state.app.currentTenant)
+
+ useEffect(() => {
+ if (tenant.defaultDomainName === 'AllTenants') {
+ setTenantColumn(false)
+ }
+ if (tenant.defaultDomainName !== 'AllTenants') {
+ setTenantColumn(true)
+ }
+ }, [tenant.defaultDomainName, tenantColumnSet])
+
+ const handleSearch = (values) => {
+ if (values.dateFilter === 'relative') {
+ setRelativeTime(`${values.Time}${values.Interval}`)
+ setStartDate(null)
+ setEndDate(null)
+ } else if (values.dateFilter === 'startEnd') {
+ setRelativeTime(null)
+ setStartDate(values.startDate)
+ setEndDate(values.endDate)
+ }
+ setVisibleA(false)
+ }
+
+ const Actions = () => {
+ return (
+
+
+
+
+
+ )
+ }
+
+ const columns = [
+ {
+ name: 'Timestamp',
+ selector: (row) => row['Timestamp'],
+ sortable: true,
+ exportSelector: 'Timestamp',
+ cell: cellDateFormatter({ format: 'short' }),
+ maxWidth: '200px',
+ },
+ {
+ name: 'Tenant',
+ selector: (row) => row['Tenant'],
+ exportSelector: 'Tenant',
+ omit: !tenantColumnSet,
+ cell: cellGenericFormatter(),
+ maxWidth: '150px',
+ },
+ {
+ name: 'Title',
+ selector: (row) => row['Title'],
+ exportSelector: 'Title',
+ cell: cellGenericFormatter(),
+ },
+ {
+ name: 'Actions',
+ cell: Actions,
+ maxWidth: '100px',
+ },
+ ]
+ return (
+
+
+
+
+
+
+ Search Options
+ setVisibleA(!visibleA)}
+ >
+
+
+
+
+
+
+
+
+
+ {
+ return (
+
+
+
+ Date Filter Type
+
+
+
+
+
+
+
+
+
+ Relative Time
+
+
+ Last
+
+
+ {({ input, meta }) => }
+
+
+ {({ input, meta }) => (
+
+ Minutes
+ Hours
+
+ Days
+
+
+ )}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Search
+
+
+
+
+ )
+ }}
+ />
+
+
+
+
+
+
+
+
+ )
+}
+
+export default ListAuditLogs
From 70853600b9183487bffe959e8168ae93b8e6d8eb Mon Sep 17 00:00:00 2001
From: John Duprey
Date: Thu, 1 Aug 2024 19:40:38 -0400
Subject: [PATCH 29/37] Add refresh config following a save
---
src/views/cipp/Extensions.jsx | 2 ++
1 file changed, 2 insertions(+)
diff --git a/src/views/cipp/Extensions.jsx b/src/views/cipp/Extensions.jsx
index eec80f5a1068..5923599a6f66 100644
--- a/src/views/cipp/Extensions.jsx
+++ b/src/views/cipp/Extensions.jsx
@@ -39,6 +39,8 @@ export default function CIPPExtensions() {
setExtensionconfig({
path: 'api/ExecExtensionsConfig',
values: values,
+ }).then((res) => {
+ listBackend({ path: 'api/ListExtensionsConfig' })
})
}
From aae375a43a93b2ca62889330c6c7f9583407f824 Mon Sep 17 00:00:00 2001
From: KelvinTegelaar
Date: Fri, 2 Aug 2024 12:23:34 +0200
Subject: [PATCH 30/37] updates to layout
---
.../tenant/standards/ListAppliedStandards.jsx | 36 ++++---------------
1 file changed, 6 insertions(+), 30 deletions(-)
diff --git a/src/views/tenant/standards/ListAppliedStandards.jsx b/src/views/tenant/standards/ListAppliedStandards.jsx
index f60370975ee6..d93d6609148c 100644
--- a/src/views/tenant/standards/ListAppliedStandards.jsx
+++ b/src/views/tenant/standards/ListAppliedStandards.jsx
@@ -751,23 +751,15 @@ const ApplyNewStandard = () => {
},
].map((template, index) => (
-
+
{template.name}
Deploy {template.name}
-
- Report
-
-
-
- Alert
-
-
Remediate
-
+
Settings
{template.templates.isSuccess && (
{
))}
-
+
Autopilot Profile
Deploy Autopilot profile
-
- Report
-
-
-
- Alert
-
-
Remediate
-
+
Settings
@@ -938,23 +922,15 @@ const ApplyNewStandard = () => {
-
+
Autopilot Status Page
Deploy Autopilot Status Page
-
- Report
-
-
-
- Alert
-
-
Remediate
-
+
Settings
From fb51fb943a128b52c05ecc8fa68cef2db29e6ca9 Mon Sep 17 00:00:00 2001
From: KelvinTegelaar
Date: Fri, 2 Aug 2024 13:31:04 +0200
Subject: [PATCH 31/37] new standards apply templates for intune
---
.../tenant/standards/ListAppliedStandards.jsx | 123 ++++++++++++++++--
1 file changed, 112 insertions(+), 11 deletions(-)
diff --git a/src/views/tenant/standards/ListAppliedStandards.jsx b/src/views/tenant/standards/ListAppliedStandards.jsx
index d93d6609148c..d591ff4b7168 100644
--- a/src/views/tenant/standards/ListAppliedStandards.jsx
+++ b/src/views/tenant/standards/ListAppliedStandards.jsx
@@ -29,7 +29,12 @@ import {
useLazyGenericGetRequestQuery,
useLazyGenericPostRequestQuery,
} from 'src/store/api/app'
-import { faCheck, faCircleNotch, faExclamationTriangle } from '@fortawesome/free-solid-svg-icons'
+import {
+ faCheck,
+ faCircleNotch,
+ faExclamationTriangle,
+ faTrash,
+} from '@fortawesome/free-solid-svg-icons'
import { CippCallout, CippContentCard, CippPage } from 'src/components/layout'
import { useSelector } from 'react-redux'
import { ModalService, validateAlphabeticalSort } from 'src/components/utilities'
@@ -368,6 +373,32 @@ const ApplyNewStandard = () => {
setEnabledWarningsCount,
])
+ const handleAddIntuneTemplate = (form) => {
+ const formvalues = form.getState().values
+ const newTemplate = {
+ label: formvalues.intunedataList.label,
+ value: formvalues.intunedataList.value,
+ AssignedTo:
+ formvalues.IntuneAssignto === 'customGroup'
+ ? formvalues.customGroup
+ : formvalues.IntuneAssignto,
+ }
+ const originalTemplates = formvalues.standards?.IntuneTemplate?.TemplateList || []
+ const updatedTemplateList = [...originalTemplates, newTemplate]
+
+ form.change('standards.IntuneTemplate.TemplateList', updatedTemplateList)
+ form.change('intunedataList', undefined)
+ form.change('intuneAssignTo', undefined)
+ form.change('customGroup', undefined)
+ }
+ const handleRemoveDeployedTemplate = (form, row) => {
+ console.log(row)
+ const formvalues = form.getState().values
+ const updatedTemplateList = formvalues.standards.IntuneTemplate.TemplateList.filter(
+ (template) => template.value !== row.value,
+ )
+ form.change('standards.IntuneTemplate.TemplateList', updatedTemplateList)
+ }
return (
<>
@@ -484,7 +515,7 @@ const ApplyNewStandard = () => {
},
}}
onSubmit={handleSubmit}
- render={({ handleSubmit, submitting, values }) => {
+ render={({ handleSubmit, submitting, values, form }) => {
return (
@@ -728,6 +759,7 @@ const ApplyNewStandard = () => {
switchName: 'standards.IntuneTemplate',
assignable: true,
templates: intuneTemplates,
+ table: true,
},
{
name: 'Transport Rule Template',
@@ -761,11 +793,53 @@ const ApplyNewStandard = () => {
Settings
+ {template.table && (
+ row['label'],
+ sortable: true,
+ exportSelector: 'name',
+ cell: cellGenericFormatter(),
+ },
+ {
+ name: 'Assigned to',
+ selector: (row) => row['AssignedTo'],
+ sortable: true,
+ exportSelector: 'GUID',
+ },
+ {
+ name: 'Actions',
+ cell: (row) => (
+
+ handleRemoveDeployedTemplate(form, row)
+ }
+ >
+
+
+ ),
+ },
+ ]}
+ />
+ )}
{template.templates.isSuccess && (
{
<>
+ handleAddIntuneTemplate(form)}>
+ Add to deployment
+
>
)}
From 5facbd7cd4fd8a2c6a1ced151439d7cfe38f3dd9 Mon Sep 17 00:00:00 2001
From: KelvinTegelaar
Date: Fri, 2 Aug 2024 13:50:31 +0200
Subject: [PATCH 32/37] add remove of old undefined.
---
src/views/tenant/standards/ListAppliedStandards.jsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/views/tenant/standards/ListAppliedStandards.jsx b/src/views/tenant/standards/ListAppliedStandards.jsx
index d591ff4b7168..10eab27c96fd 100644
--- a/src/views/tenant/standards/ListAppliedStandards.jsx
+++ b/src/views/tenant/standards/ListAppliedStandards.jsx
@@ -385,7 +385,7 @@ const ApplyNewStandard = () => {
}
const originalTemplates = formvalues.standards?.IntuneTemplate?.TemplateList || []
const updatedTemplateList = [...originalTemplates, newTemplate]
-
+ form.change('standards.IntuneTemplate.AssignTo', undefined)
form.change('standards.IntuneTemplate.TemplateList', updatedTemplateList)
form.change('intunedataList', undefined)
form.change('intuneAssignTo', undefined)
From c7b6f31f510dedb7e9b633e4986712463be08a33 Mon Sep 17 00:00:00 2001
From: KelvinTegelaar
Date: Fri, 2 Aug 2024 15:47:40 +0200
Subject: [PATCH 33/37] revert due to license column bug.
---
src/components/tables/CellLicense.jsx | 2 --
1 file changed, 2 deletions(-)
diff --git a/src/components/tables/CellLicense.jsx b/src/components/tables/CellLicense.jsx
index ff5df5329743..50b88b978c4f 100644
--- a/src/components/tables/CellLicense.jsx
+++ b/src/components/tables/CellLicense.jsx
@@ -8,8 +8,6 @@ export function CellLicense({ cell }) {
if (licenseAssignment.skuId == M365Licenses[x].GUID) {
licenses.push(M365Licenses[x].Product_Display_Name)
break
- } else {
- licenses.push(licenseAssignment.skuId)
}
}
})
From fde2501818265f9e52bbc2209be01b52bc7d3423 Mon Sep 17 00:00:00 2001
From: John Duprey
Date: Fri, 2 Aug 2024 11:36:59 -0400
Subject: [PATCH 34/37] Add message header parsing
---
.../email-exchange/tools/MessageViewer.jsx | 34 ++++++++++++++-----
1 file changed, 25 insertions(+), 9 deletions(-)
diff --git a/src/views/email-exchange/tools/MessageViewer.jsx b/src/views/email-exchange/tools/MessageViewer.jsx
index 52d7c3b637a4..f010c0b37c4f 100644
--- a/src/views/email-exchange/tools/MessageViewer.jsx
+++ b/src/views/email-exchange/tools/MessageViewer.jsx
@@ -27,6 +27,7 @@ const MessageViewer = ({ emailSource }) => {
const [emlContent, setEmlContent] = useState(null)
const [emlError, setEmlError] = useState(false)
const [messageHtml, setMessageHtml] = useState('')
+ const [emlHeaders, setEmlHeaders] = useState(null)
const getAttachmentIcon = (contentType) => {
if (contentType.includes('image')) {
@@ -126,21 +127,32 @@ const MessageViewer = ({ emailSource }) => {
return d instanceof Date && !isNaN(d)
}
- const showEmailModal = (emailSource) => {
+ const showEmailModal = (emailSource, title = 'Email Source') => {
ModalService.open({
data: emailSource,
componentType: 'codeblock',
- title: 'Email Source',
+ title: title,
size: 'lg',
})
}
- const EmailButtons = (emailSource) => {
+ const EmailButtons = (emailHeaders, emailSource) => {
+ const emailSourceBytes = new TextEncoder().encode(emailSource)
+ const blob = new Blob([emailSourceBytes], { type: 'message/rfc822' })
+ const url = URL.createObjectURL(blob)
return (
- showEmailModal(emailSource)}>
-
- View Source
-
+
+ {emailHeaders && (
+ showEmailModal(emailHeaders, 'Email Headers')} className="me-2">
+
+ View Headers
+
+ )}
+ showEmailModal(emailSource)}>
+
+ View Source
+
+
)
}
@@ -150,6 +162,7 @@ const MessageViewer = ({ emailSource }) => {
setEmlError(true)
setEmlContent(null)
setMessageHtml(null)
+ setEmlHeaders(null)
} else {
setEmlContent(ReadEmlJson)
setEmlError(false)
@@ -160,11 +173,14 @@ const MessageViewer = ({ emailSource }) => {
} else {
setMessageHtml(null)
}
+ const header_regex = /(?:^[\w-]+:\s?.*(?:\r?\n[ \t].*)*\r?\n?)+/gm
+ const headers = emailSource.match(header_regex)
+ setEmlHeaders(headers ? headers[0] : null)
}
})
- }, [emailSource, setMessageHtml, setEmlError, setEmlContent])
+ }, [emailSource, setMessageHtml, setEmlError, setEmlContent, setEmlHeaders])
- var buttons = EmailButtons(emailSource)
+ var buttons = EmailButtons(emlHeaders, emailSource)
return (
<>
From c755dce05a4775570b719b934a757166ba42d52f Mon Sep 17 00:00:00 2001
From: John Duprey
Date: Fri, 2 Aug 2024 12:31:27 -0400
Subject: [PATCH 35/37] Audit Logs
Add basic offcanvas
---
.../tenant/administration/ListAuditLogs.jsx | 63 +++++++++++++++++--
1 file changed, 57 insertions(+), 6 deletions(-)
diff --git a/src/views/tenant/administration/ListAuditLogs.jsx b/src/views/tenant/administration/ListAuditLogs.jsx
index 2ec02c53cf6c..4e1d30392b3d 100644
--- a/src/views/tenant/administration/ListAuditLogs.jsx
+++ b/src/views/tenant/administration/ListAuditLogs.jsx
@@ -26,8 +26,13 @@ import {
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { Condition, RFFCFormInput, RFFCFormRadioList } from 'src/components/forms'
import { Field, Form } from 'react-final-form'
+import { useSearchParams } from 'react-router-dom'
+import { CippCodeBlock, CippOffcanvas } from 'src/components/utilities'
const ListAuditLogs = () => {
+ // get query parameters
+ const [searchParams, setSearchParams] = useSearchParams()
+ const logId = searchParams.get('LogId')
const [interval, setInterval] = React.useState('d')
const [time, setTime] = React.useState(1)
const [relativeTime, setRelativeTime] = React.useState('1d')
@@ -59,13 +64,58 @@ const ListAuditLogs = () => {
setVisibleA(false)
}
- const Actions = () => {
+ const Actions = (row) => {
+ const [visible, setVisible] = React.useState(false)
return (
-
-
-
-
-
+ <>
+ setVisible(true)}>
+
+
+
+
+ setVisible(false)}
+ visible={visible}
+ addedClass="offcanvas-large"
+ placement="end"
+ >
+
+
+
+ Log Details
+
+
+
+ {row?.Data?.ActionText && (
+
+
+
+
+ {row?.Data?.ActionText}
+
+
+
+ )}
+
+
+ Raw Log
+
+
+
+
+
+
+ >
)
}
@@ -219,6 +269,7 @@ const ListAuditLogs = () => {
RelativeTime: relativeTime,
StartDate: startDate,
EndDate: endDate,
+ LogId: logId,
},
tableProps: {
selectableRows: true,
From 53445e671a41ba4ffd0c7b36e8c095b2e1f89493 Mon Sep 17 00:00:00 2001
From: KelvinTegelaar
Date: Fri, 2 Aug 2024 18:39:50 +0200
Subject: [PATCH 36/37] release ready
---
src/importsMap.jsx | 1 +
src/routes.json | 6 +
src/views/cipp/TemplateLibrary.jsx | 149 ++++++++++++++++++
.../intune/MEMListPolicyTemplates.jsx | 6 +
.../tenant/conditional/ListCATemplates.jsx | 6 +
5 files changed, 168 insertions(+)
create mode 100644 src/views/cipp/TemplateLibrary.jsx
diff --git a/src/importsMap.jsx b/src/importsMap.jsx
index 3708a44f4ed9..49f07b806f00 100644
--- a/src/importsMap.jsx
+++ b/src/importsMap.jsx
@@ -2,6 +2,7 @@ import React from 'react'
export const importsMap = {
"/home": React.lazy(() => import('./views/home/Home')),
"/cipp/logs": React.lazy(() => import('./views/cipp/Logs')),
+ "/cipp/template-library": React.lazy(() => import('./views/cipp/TemplateLibrary')),
"/cipp/scheduler": React.lazy(() => import('./views/cipp/Scheduler')),
"/cipp/statistics": React.lazy(() => import('./views/cipp/Statistics')),
"/cipp/404": React.lazy(() => import('./views/pages/page404/Page404')),
diff --git a/src/routes.json b/src/routes.json
index 73f0383bbd20..532343cc83e4 100644
--- a/src/routes.json
+++ b/src/routes.json
@@ -11,6 +11,12 @@
"component": "views/cipp/Logs",
"allowedRoles": ["admin", "editor", "readonly"]
},
+ {
+ "path": "/cipp/template-library",
+ "name": "Logs",
+ "component": "views/cipp/TemplateLibrary",
+ "allowedRoles": ["admin", "editor", "readonly"]
+ },
{
"path": "/cipp/scheduler",
"name": "Scheduler",
diff --git a/src/views/cipp/TemplateLibrary.jsx b/src/views/cipp/TemplateLibrary.jsx
new file mode 100644
index 000000000000..4dbf493d2293
--- /dev/null
+++ b/src/views/cipp/TemplateLibrary.jsx
@@ -0,0 +1,149 @@
+import React, { useState } from 'react'
+import { CAlert, CButton, CCallout, CCol, CForm, CRow, CSpinner, CTooltip } from '@coreui/react'
+import { useSelector } from 'react-redux'
+import { Field, Form } from 'react-final-form'
+import { RFFCFormInput, RFFCFormSwitch } from 'src/components/forms'
+import {
+ useGenericGetRequestQuery,
+ useLazyGenericGetRequestQuery,
+ useLazyGenericPostRequestQuery,
+} from 'src/store/api/app'
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
+import { faCircleNotch, faEdit, faEye } from '@fortawesome/free-solid-svg-icons'
+import { CippPage, CippPageList } from 'src/components/layout'
+import 'react-datepicker/dist/react-datepicker.css'
+import { ModalService, TenantSelector } from 'src/components/utilities'
+import arrayMutators from 'final-form-arrays'
+import { useListConditionalAccessPoliciesQuery } from 'src/store/api/tenants'
+import CippButtonCard from 'src/components/contentcards/CippButtonCard'
+import { CellTip, cellGenericFormatter } from 'src/components/tables/CellGenericFormat'
+import { cellBadgeFormatter, cellDateFormatter } from 'src/components/tables'
+import { Alert } from '@coreui/coreui'
+
+const TemplateLibrary = () => {
+ const [ExecuteGetRequest, getResults] = useLazyGenericGetRequestQuery()
+ const tenantDomain = useSelector((state) => state.app.currentTenant.defaultDomainName)
+ const [refreshState, setRefreshState] = useState(false)
+ const [genericPostRequest, postResults] = useLazyGenericPostRequestQuery()
+
+ const onSubmit = (values) => {
+ const startDate = new Date()
+ startDate.setHours(0, 0, 0, 0)
+ const unixTime = Math.floor(startDate.getTime() / 1000) - 45
+ const shippedValues = {
+ TenantFilter: tenantDomain,
+ Name: `CIPP Template ${tenantDomain}`,
+ Command: { value: `New-CIPPTemplateRun` },
+ Parameters: { TemplateSettings: { ...values } },
+ ScheduledTime: unixTime,
+ Recurrence: { value: '4h' },
+ }
+ genericPostRequest({
+ path: '/api/AddScheduledItem?DisallowDuplicateName=true',
+ values: shippedValues,
+ }).then((res) => {
+ setRefreshState(res.requestId)
+ })
+ }
+
+ const {
+ data: caPolicies = [],
+ isFetching: caIsFetching,
+ error: caError,
+ } = useListConditionalAccessPoliciesQuery({ domain: tenantDomain })
+
+ return (
+
+ <>
+
+
+
+ Set Tenant as Template Library
+ {postResults.isFetching && (
+
+ )}
+
+ }
+ title="Add Template Library"
+ icon={faEdit}
+ >
+ {
+ return (
+
+
+ Template libraries are tenants setup to retrieve the latest version of
+ policies from. By setting a tenant as a template library, automatic updates
+ will be made to the templates within CIPP based on this template library
+ every 4 hours.
+
+ Enabling this feature will overwrite templates with the same name.
+
+
+
+
+ Tenant
+
+ {(props) => }
+
+
+
+
+
+
+
+
+ Conditional Access
+
+ Intune
+
+
+
+
+
+ {postResults.isSuccess && (
+
+ {postResults.data.Results}
+
+ )}
+ {getResults.isFetching && (
+
+ Loading
+
+ )}
+ {getResults.isSuccess && (
+ {getResults.data?.Results}
+ )}
+ {getResults.isError && (
+
+ Could not connect to API: {getResults.error.message}
+
+ )}
+
+ )
+ }}
+ />
+
+
+
+ >
+
+ )
+}
+
+export default TemplateLibrary
diff --git a/src/views/endpoint/intune/MEMListPolicyTemplates.jsx b/src/views/endpoint/intune/MEMListPolicyTemplates.jsx
index 4ca9452daeff..1b389af653bc 100644
--- a/src/views/endpoint/intune/MEMListPolicyTemplates.jsx
+++ b/src/views/endpoint/intune/MEMListPolicyTemplates.jsx
@@ -16,6 +16,7 @@ import { useLazyGenericGetRequestQuery } from 'src/store/api/app'
import { CippPage } from 'src/components/layout'
import { ModalService } from 'src/components/utilities'
import CippCodeOffCanvas from 'src/components/utilities/CippCodeOffcanvas'
+import { TitleButton } from 'src/components/buttons'
//todo: expandable with RAWJson property.
@@ -106,6 +107,11 @@ const AutopilotListTemplates = () => {
Endpoint Manager Templates
+
{getResults.isFetching && (
diff --git a/src/views/tenant/conditional/ListCATemplates.jsx b/src/views/tenant/conditional/ListCATemplates.jsx
index 555a595c3ddb..13b652854a42 100644
--- a/src/views/tenant/conditional/ListCATemplates.jsx
+++ b/src/views/tenant/conditional/ListCATemplates.jsx
@@ -15,6 +15,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { useLazyGenericGetRequestQuery } from 'src/store/api/app'
import { CippPage } from 'src/components/layout'
import { ModalService, CippCodeOffCanvas } from 'src/components/utilities'
+import { TitleButton } from 'src/components/buttons'
//todo: expandable with RAWJson property.
@@ -87,6 +88,11 @@ const AutopilotListTemplates = () => {
Results
+
{getResults.isFetching && (
From d0b1f4f956cfdb2e41469e9425cdf32f51933890 Mon Sep 17 00:00:00 2001
From: KelvinTegelaar
Date: Fri, 2 Aug 2024 19:28:17 +0200
Subject: [PATCH 37/37] update
---
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 169a8fbc8a05..dde03c41dba7 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "cipp",
- "version": "6.1.1",
+ "version": "6.2.0",
"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 f3b5af39e430..6abaeb2f9072 100644
--- a/public/version_latest.txt
+++ b/public/version_latest.txt
@@ -1 +1 @@
-6.1.1
+6.2.0
diff --git a/version_latest.txt b/version_latest.txt
index f3b5af39e430..6abaeb2f9072 100644
--- a/version_latest.txt
+++ b/version_latest.txt
@@ -1 +1 @@
-6.1.1
+6.2.0