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/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/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)
}
}
})
diff --git a/src/importsMap.jsx b/src/importsMap.jsx
index f7c2a6e83234..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')),
@@ -42,7 +43,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..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",
@@ -279,11 +285,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/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' })
})
}
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}
+ >
+
+
+
+ >
+
+ )
+}
+
+export default TemplateLibrary
diff --git a/src/views/cipp/app-settings/SettingsSuperAdmin.jsx b/src/views/cipp/app-settings/SettingsSuperAdmin.jsx
index 18cb6b397980..e6c7c8facc8e 100644
--- a/src/views/cipp/app-settings/SettingsSuperAdmin.jsx
+++ b/src/views/cipp/app-settings/SettingsSuperAdmin.jsx
@@ -66,6 +66,42 @@ export function SettingsSuperAdmin() {
+
+
+ Tenant Mode
+
+
>
>
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 (
<>
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/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={
{
+ // 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')
+ 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 = (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
+
+
+
+
+
+
+ >
+ )
+ }
+
+ 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)}
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+export default ListAuditLogs
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 && (
diff --git a/src/views/tenant/standards/ListAppliedStandards.jsx b/src/views/tenant/standards/ListAppliedStandards.jsx
index f60370975ee6..10eab27c96fd 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.AssignTo', undefined)
+ 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',
@@ -751,29 +783,63 @@ const ApplyNewStandard = () => {
},
].map((template, index) => (
-
+
{template.name}
Deploy {template.name}
-
- Report
-
-
-
- Alert
-
-
Remediate
-
+
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
+
>
)}
))}
-
+
Autopilot Profile
Deploy Autopilot profile
-
- Report
-
-
-
- Alert
-
-
Remediate
-
+
Settings
@@ -938,23 +1023,15 @@ const ApplyNewStandard = () => {
-
+
Autopilot Status Page
Deploy Autopilot Status Page
-
- Report
-
-
-
- Alert
-
-
Remediate
-
+
Settings
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