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} + > +
{ + 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. + +

+ + + + + {(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/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

    + ( + <> + {partnerConfig.isFetching && } + + + + + + + )} + /> + {webhookCreateResult.isSuccess && ( + + {webhookCreateResult?.data?.results} + + )} +
    +
    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)} + > + + + + + + + + + + { + return ( + + + + Date Filter Type +
    + +
    +
    +
    +
    + + + + Relative Time + + + Last + + + {({ input, meta }) => } + + + {({ input, meta }) => ( + + + + + + )} + + + + + + + + + + + + + + + + + + + + Search + + + +
    + ) + }} + /> +
    +
    +
    +
    +
    +
    + +
    + ) +} + +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