diff --git a/package-lock.json b/package-lock.json index 232261d7bcc3..490fb9a392b6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "cipp", - "version": "5.5.0", + "version": "5.6.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "cipp", - "version": "5.5.0", + "version": "5.6.1", "license": "AGPL-3.0", "dependencies": { "@coreui/chartjs": "^3.0.0", @@ -46,6 +46,7 @@ "react": "^18.2.0", "react-app-polyfill": "^2.0.0", "react-bootstrap": "^1.6.5", + "react-circular-progressbar": "^2.1.0", "react-copy-to-clipboard": "^5.1.0", "react-data-table-component": "^7.4.5", "react-datepicker": "^4.10.0", @@ -6864,6 +6865,14 @@ "react-dom": ">=16.8.0" } }, + "node_modules/react-circular-progressbar": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/react-circular-progressbar/-/react-circular-progressbar-2.1.0.tgz", + "integrity": "sha512-xp4THTrod4aLpGy68FX/k1Q3nzrfHUjUe5v6FsdwXBl3YVMwgeXYQKDrku7n/D6qsJA9CuunarAboC2xCiKs1g==", + "peerDependencies": { + "react": "^0.14.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/react-copy-to-clipboard": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/react-copy-to-clipboard/-/react-copy-to-clipboard-5.1.0.tgz", diff --git a/package.json b/package.json index 5286527775d3..332ac7504f6e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cipp", - "version": "5.6.1", + "version": "5.7.0", "description": "The CyberDrain Improved Partner Portal is a portal to help manage administration for Microsoft Partners.", "homepage": "https://cipp.app/", "bugs": { @@ -64,6 +64,7 @@ "react": "^18.2.0", "react-app-polyfill": "^2.0.0", "react-bootstrap": "^1.6.5", + "react-circular-progressbar": "^2.1.0", "react-copy-to-clipboard": "^5.1.0", "react-data-table-component": "^7.4.5", "react-datepicker": "^4.10.0", diff --git a/public/GDAPRoles.json b/public/GDAPRoles.json index adc846cf3028..bf14e31159e5 100644 --- a/public/GDAPRoles.json +++ b/public/GDAPRoles.json @@ -671,6 +671,22 @@ "Name": "Virtual Visits Administrator", "ObjectId": "e300d9e7-4a2b-4295-9eff-f1c78b36cc98" }, + { + "ExtensionData": {}, + "Description": "Manage and configure all aspects of Microsoft Viva Goals.", + "IsEnabled": true, + "IsSystem": true, + "Name": "Viva Goals Administrator", + "ObjectId": "92b086b3-e367-4ef2-b869-1de128fb986e" + }, + { + "ExtensionData": {}, + "Description": "Can manage all settings for Microsoft Viva Pulse app.", + "IsEnabled": true, + "IsSystem": true, + "Name": "Viva Pulse Administrator", + "ObjectId": "87761b17-1ed2-4af3-9acd-92a150038160" + }, { "ExtensionData": {}, "Description": "Can provision and manage all aspects of Cloud PCs.", diff --git a/public/version_latest.txt b/public/version_latest.txt index b7c75422bc1d..42cdd0b540f9 100644 --- a/public/version_latest.txt +++ b/public/version_latest.txt @@ -1 +1 @@ -5.6.1 +5.7.0 diff --git a/src/_nav.jsx b/src/_nav.jsx index 01007acb414a..902a8390b79f 100644 --- a/src/_nav.jsx +++ b/src/_nav.jsx @@ -129,14 +129,9 @@ const _nav = [ }, { component: CNavItem, - name: 'Alerts (Classic)', + name: 'Alerts', to: '/tenant/administration/alertsqueue', }, - { - component: CNavItem, - name: 'Alert Rules', - to: '/tenant/administration/AlertRules', - }, { component: CNavItem, name: 'Enterprise Applications', @@ -155,7 +150,7 @@ const _nav = [ { component: CNavItem, name: 'Tenant Onboarding', - to: '/tenant/administration/tenant-onboarding-wizard', + to: '/tenant/administration/tenant-onboarding', }, { component: CNavItem, @@ -707,6 +702,25 @@ const _nav = [ }, ], }, + { + component: CNavGroup, + name: ' Room Management', + section: 'Email & Exchange', + to: '/rooms/management', + icon: , + items: [ + { + component: CNavItem, + name: 'Rooms', + to: '/rooms/management/list-rooms', + }, + { + component: CNavItem, + name: 'Room Lists', + to: '/rooms/management/room-lists', + }, + ], + }, { component: CNavGroup, name: 'Reports', diff --git a/src/components/contentcards/CippAccordionItem.jsx b/src/components/contentcards/CippAccordionItem.jsx new file mode 100644 index 000000000000..98090b58c0c7 --- /dev/null +++ b/src/components/contentcards/CippAccordionItem.jsx @@ -0,0 +1,49 @@ +import React from 'react' +import { + CAccordionBody, + CAccordionHeader, + CAccordionItem, + CCard, + CCardBody, + CCardFooter, + CCardHeader, + CCardTitle, +} from '@coreui/react' +import Skeleton from 'react-loading-skeleton' +import PropTypes from 'prop-types' + +export default function CippAccordionItem({ + title, + titleType = 'normal', + CardButton, + children, + isFetching, +}) { + return ( + + {title} + + + + + {titleType === 'big' ?

{title}

: title} +
+
+ + {isFetching && } + {children} + + {CardButton} +
+
+
+ ) +} + +CippAccordionItem.propTypes = { + title: PropTypes.string.isRequired, + titleType: PropTypes.string, + CardButton: PropTypes.element.isRequired, + children: PropTypes.element.isRequired, + isFetching: PropTypes.bool.isRequired, +} diff --git a/src/components/contentcards/CippButtonCard.jsx b/src/components/contentcards/CippButtonCard.jsx index 1540c09a888f..4a6400ad9cca 100644 --- a/src/components/contentcards/CippButtonCard.jsx +++ b/src/components/contentcards/CippButtonCard.jsx @@ -1,7 +1,7 @@ import React from 'react' -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { CCard, CCardBody, CCardFooter, CCardHeader, CCardTitle } from '@coreui/react' import Skeleton from 'react-loading-skeleton' +import PropTypes from 'prop-types' export default function CippButtonCard({ title, @@ -25,3 +25,11 @@ export default function CippButtonCard({ ) } + +CippButtonCard.propTypes = { + title: PropTypes.string.isRequired, + titleType: PropTypes.string, + CardButton: PropTypes.element.isRequired, + children: PropTypes.element.isRequired, + isFetching: PropTypes.bool.isRequired, +} diff --git a/src/components/contentcards/CippChartCard.jsx b/src/components/contentcards/CippChartCard.jsx new file mode 100644 index 000000000000..c708ef8e51ad --- /dev/null +++ b/src/components/contentcards/CippChartCard.jsx @@ -0,0 +1,86 @@ +import React from 'react' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { CButton, CCard, CCardBody, CCardHeader, CCardTitle } from '@coreui/react' +import { CChart } from '@coreui/react-chartjs' +import { getStyle } from '@coreui/utils' +import PropTypes from 'prop-types' + +export default function CippChartCard({ + title, + titleType = 'normal', + ChartData, + ChartLabels, + ChartType = 'pie', + LegendLocation = 'bottom', + isFetching, + refreshFunction, +}) { + return ( + + + + {titleType === 'big' ?

{title}

: title} + {refreshFunction ? ( + + + + ) : ( + + + + )} +
+
+ + {ChartData && ( + + )} + +
+ ) +} +CippChartCard.propTypes = { + title: PropTypes.string.isRequired, + titleType: PropTypes.oneOf(['normal', 'big']), + ChartData: PropTypes.array.isRequired, + ChartLabels: PropTypes.array.isRequired, + ChartType: PropTypes.oneOf(['pie', 'bar', 'line']), + LegendLocation: PropTypes.oneOf(['top', 'bottom', 'left', 'right']), + isFetching: PropTypes.bool, + refreshFunction: PropTypes.func, +} diff --git a/src/components/contentcards/CippPrettyCard.jsx b/src/components/contentcards/CippPrettyCard.jsx new file mode 100644 index 000000000000..b97516e3713c --- /dev/null +++ b/src/components/contentcards/CippPrettyCard.jsx @@ -0,0 +1,46 @@ +import React from 'react' +import { CCard, CCardBody, CCardFooter, CCardHeader, CCardTitle, CCol, CRow } from '@coreui/react' +import Skeleton from 'react-loading-skeleton' +import { CircularProgressbar } from 'react-circular-progressbar' +import 'react-circular-progressbar/dist/styles.css' + +export default function CippPrettyCard({ + title, + titleType = 'normal', + percentage, + topLabel, + smallLabel, + ringcolor = '#f89226', + isFetching, +}) { + return ( + + + + {titleType === 'big' ?

{title}

: title} +
+
+ + {isFetching && } + {!isFetching && ( + + +
+ +
+
+ +

{topLabel}

+ {smallLabel} +
+
+ )} +
+
+ ) +} diff --git a/src/components/forms/RFFComponents.jsx b/src/components/forms/RFFComponents.jsx index b3f22f8cda14..737333c7dfc9 100644 --- a/src/components/forms/RFFComponents.jsx +++ b/src/components/forms/RFFComponents.jsx @@ -123,7 +123,9 @@ export const RFFCFormSwitch = ({ >
(
{ + return ( + <> +
+ {options?.map((option, key) => { + return ( + + {({ input }) => { + return ( + <> + + + ) + }} + + ) + })} +
+ + ) +} + +RFFCFormRadioList.propTypes = { + ...sharedPropTypes, + inline: PropTypes.bool, +} + export const RFFCFormTextarea = ({ name, label, diff --git a/src/components/forms/index.js b/src/components/forms/index.js index 616687f88a0f..00a76ee4554a 100644 --- a/src/components/forms/index.js +++ b/src/components/forms/index.js @@ -5,6 +5,7 @@ import { RFFCFormSwitch, RFFCFormInput, RFFCFormRadio, + RFFCFormRadioList, RFFCFormTextarea, RFFCFormSelect, RFFSelectSearch, @@ -18,6 +19,7 @@ export { RFFCFormSwitch, RFFCFormInput, RFFCFormRadio, + RFFCFormRadioList, RFFCFormTextarea, RFFCFormSelect, RFFSelectSearch, diff --git a/src/components/tables/CellBadge.jsx b/src/components/tables/CellBadge.jsx index ae6b948ce83f..03552866c817 100644 --- a/src/components/tables/CellBadge.jsx +++ b/src/components/tables/CellBadge.jsx @@ -1,6 +1,6 @@ import PropTypes from 'prop-types' import React from 'react' -import { CBadge } from '@coreui/react' +import { CBadge, CCol, CRow } from '@coreui/react' export const CellBadge = ({ label = '', color = '', children, ...rest }) => { //Create a case select, and return the color based on the label @@ -19,14 +19,28 @@ export const CellBadge = ({ label = '', color = '', children, ...rest }) => { break case 'running': color = 'primary' + break } + //if a label contains a comma, split it, and return multiple badges, if not, return one badge. force the badges to be on their own line - return ( - - {label} - {children} - - ) + if (label.includes(',')) { + const labels = label.split(',') + return labels.map((label, idx) => ( + <> + + {label} + {children} + + + )) + } else { + return ( + + {label} + {children} + + ) + } } CellBadge.propTypes = { diff --git a/src/components/tables/CellTable.jsx b/src/components/tables/CellTable.jsx index 0fffd6da5637..cfcbd5403065 100644 --- a/src/components/tables/CellTable.jsx +++ b/src/components/tables/CellTable.jsx @@ -20,20 +20,24 @@ export default function cellTable( columnProp = column } - if (!Array.isArray(columnProp) && typeof columnProp === 'object') { - columnProp = Object.keys(columnProp).map((key) => { - return { - Key: key, - Value: columnProp[key], - } - }) + if (columnProp === undefined || columnProp === null) { + columnProp = [] } else { - if (Array.isArray(columnProp) && typeof columnProp[0] !== 'object') { - columnProp = columnProp.map((row) => { + if (!Array.isArray(columnProp) && typeof columnProp === 'object') { + columnProp = Object.keys(columnProp).map((key) => { return { - Value: row, + Key: key, + Value: columnProp[key], } }) + } else { + if (Array.isArray(columnProp) && typeof columnProp[0] !== 'object') { + columnProp = columnProp.map((row) => { + return { + Value: row, + } + }) + } } } diff --git a/src/components/tables/CippTable.jsx b/src/components/tables/CippTable.jsx index 3ba432f63677..f3ea486ee243 100644 --- a/src/components/tables/CippTable.jsx +++ b/src/components/tables/CippTable.jsx @@ -38,6 +38,7 @@ import { debounce } from 'lodash-es' import { useSearchParams } from 'react-router-dom' import CopyToClipboard from 'react-copy-to-clipboard' import { setDefaultColumns } from 'src/store/features/app' +import { CippCallout } from '../layout' const FilterComponent = ({ filterText, onFilter, onClear, filterlist, onFilterPreset }) => ( <> @@ -124,6 +125,7 @@ export default function CippTable({ filterlist, showFilter = true, endpointName, + defaultSortAsc = true, tableProps: { keyField = 'id', theme = 'cyberdrain', @@ -614,7 +616,7 @@ export default function CippTable({ className="m-1" size="sm" > - + , ]) @@ -888,7 +890,7 @@ export default function CippTable({ {(updatedColumns || !dynamicColumns) && ( <> {(massResults.length >= 1 || loopRunning) && ( - + {massResults[0]?.data?.Metadata?.Heading && ( {massResults.map((message, idx) => { @@ -963,7 +965,7 @@ export default function CippTable({ )} - + )} { const [offcanvasVisible, setOffcanvasVisible] = useState(false) @@ -154,6 +155,28 @@ export default function CippActionsOffcanvas(props) { title: 'Info', size: 'lg', }) + } else if (modalType === 'table') { + const QueryColumns = [] + const columns = Object.keys(modalBody[0]).map((key) => { + QueryColumns.push({ + name: key, + selector: (row) => row[key], + sortable: true, + exportSelector: key, + cell: cellGenericFormatter(), + }) + }) + + ModalService.open({ + data: modalBody, + componentType: 'table', + componentProps: { + columns: QueryColumns, + keyField: 'SKU', + }, + title: 'Info', + size: 'lg', + }) } else { ModalService.confirm({ key: modalContent, @@ -389,7 +412,7 @@ export default function CippActionsOffcanvas(props) { {getResults.isError && ( Could not connect to API: {getResults.error.message} )} - {!cardContent && ( + {!cardContent && props?.extendedInfo && props?.extendedInfo?.length > 0 && ( diff --git a/src/components/utilities/CippCopyToClipboard.jsx b/src/components/utilities/CippCopyToClipboard.jsx index 064392d9b4fa..fa930968f7a6 100644 --- a/src/components/utilities/CippCopyToClipboard.jsx +++ b/src/components/utilities/CippCopyToClipboard.jsx @@ -14,7 +14,7 @@ function CippCopyToClipboard({ text }) { onCodeCopied()}> diff --git a/src/components/utilities/CippTableOffcanvas.jsx b/src/components/utilities/CippTableOffcanvas.jsx index bc603ecdfda4..236ccff04b4e 100644 --- a/src/components/utilities/CippTableOffcanvas.jsx +++ b/src/components/utilities/CippTableOffcanvas.jsx @@ -14,7 +14,7 @@ function CippTableOffcanvas({ tableProps, data = null, }) { - if (data !== null && data !== undefined) { + if (Array.isArray(data) && data !== null && data !== undefined && data?.length > 0) { if (!Array.isArray(data) && typeof data === 'object') { data = Object.keys(data).map((key) => { return { @@ -53,7 +53,7 @@ function CippTableOffcanvas({ hideFunction={hideFunction} > <> - {data !== null && data !== undefined ? ( + {Array.isArray(data) && data !== null && data !== undefined ? ( ) : ( diff --git a/src/components/utilities/TenantSelectorMultiple.jsx b/src/components/utilities/TenantSelectorMultiple.jsx index 4ff2f2cb8935..63f560f7f4a0 100644 --- a/src/components/utilities/TenantSelectorMultiple.jsx +++ b/src/components/utilities/TenantSelectorMultiple.jsx @@ -4,12 +4,15 @@ import Select from 'react-select' import PropTypes from 'prop-types' const TenantSelectorMultiple = React.forwardRef( - ({ values = [], onChange = () => {}, ...rest }, ref) => { + ( + { values = [], onChange = () => {}, AllTenants = false, valueIsDomain = false, ...rest }, + ref, + ) => { const { data: tenants = [], isLoading, error, - } = useListTenantsQuery({ showAllTenantsSelector: false }) + } = useListTenantsQuery({ showAllTenantSelector: AllTenants }) let placeholder = 'Select Tenants' if (isLoading) { @@ -33,6 +36,7 @@ const TenantSelectorMultiple = React.forwardRef( options={tenants.map(({ customerId, defaultDomainName, displayName }) => ({ value: customerId, label: [displayName] + [` (${defaultDomainName})`], + fullValue: { customerId, defaultDomainName, displayName }, }))} multiple printOptions="on-focus" @@ -44,6 +48,8 @@ const TenantSelectorMultiple = React.forwardRef( TenantSelectorMultiple.propTypes = { onChange: PropTypes.func, + AllTenants: PropTypes.bool, + valueIsDomain: PropTypes.bool, values: PropTypes.arrayOf(PropTypes.string).isRequired, } diff --git a/src/data/AuditLogSchema.json b/src/data/AuditLogSchema.json new file mode 100644 index 000000000000..d29cdb5cffea --- /dev/null +++ b/src/data/AuditLogSchema.json @@ -0,0 +1,969 @@ +{ + "Common": { + "Id": "String", + "RecordType": "List:AuditLogRecordType", + "CreationTime": "String", + "Operation": "List:Operation", + "OrganizationId": "String", + "UserType": "List:UserType", + "UserKey": "String", + "Workload": "String", + "ResultStatus": "String", + "ObjectId": "String", + "UserId": "String", + "ClientIP": "String", + "Username": "String", + "CIPPGeoLocation": "List:countryList", + "CIPPBadRepIP": "String", + "CIPPHostedIP": "String", + "CIPPIPDetected": "String" + }, + "Audit.Exchange": { + "Id": "Combination GUID", + "RecordType": "List:AuditLogRecordType", + "CreationTime": "String", + "Operation": "List:Operation", + "OrganizationId": "Guid", + "UserType": "List:UserType", + "UserKey": "String", + "Workload": "String", + "ResultStatus": "String", + "ObjectId": "String", + "UserId": "String", + "ClientIP": "String", + "Scope": "List:AuditLogScope", + "ModifiedObjectResolvedname": "String", + "Parameters": "Common.namevaluePair", + "ExternalAccess": "Boolean", + "OriginatingServer": "String", + "Organizationname": "String", + "LogonType": "LogonType", + "InternalLogonType": "List:LogonType", + "MailboxGuid": "String", + "MailboxOwnerUPN": "String", + "MailboxOwnerSid": "String", + "MailboxOwnerMasterAccountSid": "String", + "LogonUserSid": "String", + "LogonUserDisplayname": "String", + "ClientInfoString": "String", + "ClientIPAddress": "String", + "ClientMachinename": "String", + "ClientProcessname": "String", + "ClientVersion": "String", + "MoveToFolder": "String" + }, + "Audit.AzureActiveDirectory": { + "AzureActiveDirectoryEventType": "List:AzureActiveDirectoryEventType", + "AccountEnabled": "String", + "Actor": "List:IdentityTypevaluePair", + "ActorContextId": "String", + "ActorIpAddress": "String", + "InterSystemsId": "String", + "IntraSystemsId": "String", + "SupportTicketId": "String", + "Target": "List:IdentityTypevaluePair", + "TargetContextId": "String", + "ApplicationId": "String", + "Client": "String", + "Errorvalue": "String", + "LogonError": "String" + }, + "List:Operation": [ + { "value": "UserLoggedIn", "name": "A user logged in" }, + { "value": "accessed mailbox items", "name": "accessed mailbox items" }, + { "value": "add delegation entry.", "name": "added delegation entry" }, + { "value": "add domain to company.", "name": "added domain to company" }, + { "value": "add group.", "name": "added group" }, + { "value": "add member to group.", "name": "added member to group" }, + { "value": "add mailboxpermission", "name": "added delegate mailbox permissions" }, + { "value": "add member to role.", "name": "added member to role" }, + { "value": "add partner to company.", "name": "added a partner to the directory" }, + { "value": "add service principal.", "name": "added service principal" }, + { + "value": "add service principal credentials.", + "name": "added credentials to a service principal" + }, + { "value": "add user.", "name": "added user" }, + { "value": "addfolderpermissions", "name": "added permissions to folder" }, + { "value": "applyrecordlabel", "name": "labeled message as a record" }, + { "value": "change user license.", "name": "changed user license" }, + { "value": "change user password.", "name": "changed user password" }, + { "value": "copy", "name": "copied messages to another folder" }, + { "value": "create", "name": "created mailbox item" }, + { "value": "delete group.", "name": "deleted group" }, + { "value": "delete user.", "name": "deleted user" }, + { "value": "harddelete", "name": "purged messages from the mailbox" }, + { "value": "mailboxlogin", "name": "user signed in to mailbox" }, + { "value": "move", "name": "moved messages to another folder" }, + { "value": "movetodeleteditems", "name": "moved messages to deleted items folder" }, + { "value": "new-inboxrule", "name": "created new inbox rule in outlook web app" }, + { "value": "remove delegation entry.", "name": "removed delegation entry" }, + { "value": "remove domain from company.", "name": "removed domain from company" }, + { "value": "remove member from group.", "name": "removed member from group" }, + { "value": "remove member from a role.", "name": "remove member from a role" }, + { "value": "Disable Strong Authentication.", "name": "Disable Strong Authentication." }, + + { + "value": "remove service principal.", + "name": "removed a service principal from the directory" + }, + { + "value": "remove service principal credentials.", + "name": "removed credentials from a service principal" + }, + { "value": "remove mailboxpermission", "name": "removed delegate mailbox permissions" }, + { "value": "remove member from role.", "name": "removed a user from a directory role" }, + { "value": "remove partner from company.", "name": "removed a partner from the directory" }, + { "value": "removefolderpermissions", "name": "removed permissions from folder" }, + { "value": "reset user password.", "name": "reset user password" }, + { "value": "send", "name": "sent message" }, + { "value": "sendas", "name": "sent message using send as permissions" }, + { "value": "sendonbehalf", "name": "sent message using send on behalf permissions" }, + { "value": "set company contact information.", "name": "set company contact information" }, + { "value": "set company information.", "name": "set company information" }, + { "value": "set delegation entry.", "name": "set delegation entry" }, + { "value": "set dirsyncenabled flag.", "name": "turned on azure ad sync" }, + { "value": "set domain authentication.", "name": "set domain authentication" }, + { + "value": "set federation settings on domain.", + "name": "updated the federation settings for a domain" + }, + { + "value": "set force change user password.", + "name": "set property that forces user to change password" + }, + { "value": "set inboxrule", "name": "modified inbox rule from outlook web app" }, + { "value": "set license properties.", "name": "set license properties" }, + { "value": "set password policy.", "name": "set password policy" }, + { "value": "softdelete", "name": "deleted messages from deleted items folder" }, + { "value": "update", "name": "updated message" }, + { "value": "update user.", "name": "updated user" }, + { "value": "update group.", "name": "updated group" }, + { "value": "update domain.", "name": "updated domain" }, + { + "value": "updatecalendardelegation", + "name": "added or removed user with delegate access to calendar folder" + }, + { "value": "updatefolderpermissions", "name": "modified folder permission" }, + { "value": "updateinboxrules", "name": "updated inbox rules from outlook client" }, + { "value": "verify domain.", "name": "verified domain" }, + { "value": "verify email verified domain.", "name": "verified email verified domain" }, + { + "value": "Update StsRefreshTokenValidFrom Timestamp.", + "name": "Update StsRefreshTokenValidFrom Timestamp." + } + ], + "List:LogonType": [ + { "value": 0, "Membername": "Owner", "name": "The mailbox owner." }, + { + "value": 1, + "Membername": "Admin", + "name": "A person with administrative privileges for someone's mailbox." + }, + { + "value": 2, + "Membername": "Delegated", + "name": "A person with delegate privileges for someone's mailbox." + }, + { + "value": 3, + "Membername": "Transport", + "name": "A transport service in the Microsoft datacenter." + }, + { + "value": 4, + "Membername": "SystemService", + "name": "A service account in the Microsoft datacenter" + }, + { "value": 5, "Membername": "BestAccess", "name": "Reserved for internal use." }, + { "value": 6, "Membername": "DelegatedAdmin", "name": "A delegated administrator." } + ], + "List:UserType": [ + { "value": 0, "Membername": "Regular", "name": "A regular user." }, + { "value": 1, "Membername": "Reserved", "name": "A reserved user." }, + { "value": 2, "Membername": "Admin", "name": "An administrator." }, + { "value": 3, "Membername": "DcAdmin", "name": "A Microsoft datacenter operator." }, + { "value": 4, "Membername": "System", "name": "A system account." }, + { "value": 5, "Membername": "Application", "name": "An application." }, + { "value": 6, "Membername": "ServicePrincipal", "name": "A service principal." }, + { "value": 7, "Membername": "CustomPolicy", "name": "A custom policy." }, + { "value": 8, "Membername": "SystemPolicy", "name": "A system policy." } + ], + "List:AuditLogRecordType": [ + { + "value": 1, + "Membername": "ExchangeAdmin", + "name": "Events from the Exchange admin audit log." + }, + { + "value": 2, + "Membername": "ExchangeItem", + "name": "Events from an Exchange mailbox audit log for actions that are performed on a single item, such as creating or receiving an email message." + }, + { + "value": 3, + "Membername": "ExchangeItemGroup", + "name": "Events from an Exchange mailbox audit log for actions that can be performed on multiple items, such as moving or deleted one or more email messages." + }, + { "value": 4, "Membername": "SharePoint", "name": "SharePoint events." }, + { + "value": 6, + "Membername": "SharePointFileOperation", + "name": "SharePoint file operation events." + }, + { "value": 7, "Membername": "OneDrive", "name": "OneDrive for Business events." }, + { + "value": 8, + "Membername": "AzureActiveDirectory", + "name": "Microsoft Entra ID events." + }, + { + "value": 9, + "Membername": "AzureActiveDirectoryAccountLogon", + "name": "Microsoft Entra ID OrgId logon events (deprecated)." + }, + { + "value": 10, + "Membername": "DataCenterSecurityCmdlet", + "name": "Data Center security cmdlet events." + }, + { + "value": 11, + "Membername": "ComplianceDLPSharePoint", + "name": "Data loss protection (DLP) events in SharePoint and OneDrive for Business." + }, + { + "value": 13, + "Membername": "ComplianceDLPExchange", + "name": "Data loss protection (DLP) events in Exchange, when configured via Unified DLP Policy. DLP events based on Exchange Transport Rules are not supported." + }, + { + "value": 14, + "Membername": "SharePointSharingOperation", + "name": "SharePoint sharing events." + }, + { + "value": 15, + "Membername": "AzureActiveDirectoryStsLogon", + "name": "Secure Token Service (STS) logon events in Microsoft Entra ID." + }, + { + "value": 16, + "Membername": "SkypeForBusinessPSTNUsage", + "name": "Public Switched Telephone Network (PSTN) events from Skype for Business." + }, + { + "value": 17, + "Membername": "SkypeForBusinessUsersBlocked", + "name": "Blocked user events from Skype for Business." + }, + { + "value": 18, + "Membername": "SecurityComplianceCenterEOPCmdlet", + "name": "Admin actions from the Security & Compliance Center." + }, + { + "value": 19, + "Membername": "ExchangeAggregatedOperation", + "name": "Aggregated Exchange mailbox auditing events." + }, + { "value": 20, "Membername": "PowerBIAudit", "name": "Power BI events." }, + { "value": 21, "Membername": "CRM", "name": "Dynamics 365 events." }, + { "value": 22, "Membername": "Yammer", "name": "Yammer events." }, + { + "value": 23, + "Membername": "SkypeForBusinessCmdlets", + "name": "Skype for Business events." + }, + { + "value": 24, + "Membername": "Discovery", + "name": "Events for eDiscovery activities performed by running content searches and managing eDiscovery cases in the Security & Compliance Center." + }, + { "value": 25, "Membername": "MicrosoftTeams", "name": "Events from Microsoft Teams." }, + { + "value": 28, + "Membername": "ThreatIntelligence", + "name": "Phishing and malware events from Exchange Online Protection and Microsoft Defender for Office 365." + }, + { + "value": 29, + "Membername": "MailSubmission", + "name": "Submission events from Exchange Online Protection and Microsoft Defender for Office 365." + }, + { + "value": 30, + "Membername": "MicrosoftFlow", + "name": "Microsoft Power Automate (formerly called Microsoft Flow) events." + }, + { "value": 31, "Membername": "AeD", "name": "Advanced eDiscovery events." }, + { "value": 32, "Membername": "MicrosoftStream", "name": "Microsoft Stream events." }, + { + "value": 33, + "Membername": "ComplianceDLPSharePointClassification", + "name": "Events related to DLP classification in SharePoint." + }, + { + "value": 34, + "Membername": "ThreatFinder", + "name": "Campaign-related events from Microsoft Defender for Office 365." + }, + { "value": 35, "Membername": "Project", "name": "Microsoft Project events." }, + { + "value": 36, + "Membername": "SharePointListOperation", + "name": "SharePoint List events." + }, + { + "value": 37, + "Membername": "SharePointCommentOperation", + "name": "SharePoint comment events." + }, + { + "value": 38, + "Membername": "DataGovernance", + "name": "Events related to retention policies and retention labels in the Security & Compliance Center" + }, + { "value": 39, "Membername": "Kaizala", "name": "Kaizala events." }, + { + "value": 40, + "Membername": "SecurityComplianceAlerts", + "name": "Security and compliance alert signals." + }, + { + "value": 41, + "Membername": "ThreatIntelligenceUrl", + "name": "Safe links time-of-block and block override events from Microsoft Defender for Office 365." + }, + { + "value": 42, + "Membername": "SecurityComplianceInsights", + "name": "Events related to insights and reports in the Office 365 security and compliance center." + }, + { + "value": 43, + "Membername": "MIPLabel", + "name": "Events related to the detection in the Transport pipeline of email messages that have been tagged (manually or automatically) with sensitivity labels." + }, + { + "value": 44, + "Membername": "WorkplaceAnalytics", + "name": "Workplace Analytics events." + }, + { "value": 45, "Membername": "PowerAppsApp", "name": "Power Apps events." }, + { + "value": 46, + "Membername": "PowerAppsPlan", + "name": "Subscription plan events for Power Apps." + }, + { + "value": 47, + "Membername": "ThreatIntelligenceAtpContent", + "name": "Phishing and malware events for files in SharePoint, OneDrive for Business, and Microsoft Teams from Microsoft Defender for Office 365." + }, + { + "value": 48, + "Membername": "LabelContentExplorer", + "name": "Events related to data classification content explorer." + }, + { + "value": 49, + "Membername": "TeamsHealthcare", + "name": "Events related to the Patients application in Microsoft Teams for Healthcare." + }, + { + "value": 50, + "Membername": "ExchangeItemAggregated", + "name": "Events related to the MailItemsAccessed mailbox auditing action." + }, + { + "value": 51, + "Membername": "HygieneEvent", + "name": "Events related to outbound spam protection." + }, + { + "value": 52, + "Membername": "DataInsightsRestApiAudit", + "name": "Data Insights REST API events." + }, + { + "value": 53, + "Membername": "InformationBarrierPolicyApplication", + "name": "Events related to the application of information barrier policies." + }, + { + "value": 54, + "Membername": "SharePointListItemOperation", + "name": "SharePoint list item events." + }, + { + "value": 55, + "Membername": "SharePointContentTypeOperation", + "name": "SharePoint list content type events." + }, + { + "value": 56, + "Membername": "SharePointFieldOperation", + "name": "SharePoint list field events." + }, + { "value": 57, "Membername": "MicrosoftTeamsAdmin", "name": "Teams admin events." }, + { + "value": 58, + "Membername": "HRSignal", + "name": "Events related to HR data signals that support the Insider risk management solution." + }, + { "value": 59, "Membername": "MicrosoftTeamsDevice", "name": "Teams device events." }, + { + "value": 60, + "Membername": "MicrosoftTeamsAnalytics", + "name": "Teams analytics events." + }, + { + "value": 61, + "Membername": "InformationWorkerProtection", + "name": "Events related to compromised user alerts." + }, + { + "value": 62, + "Membername": "Campaign", + "name": "Email campaign events from Microsoft Defender for Office 365." + }, + { "value": 63, "Membername": "DLPEndpoint", "name": "Endpoint DLP events." }, + { + "value": 64, + "Membername": "AirInvestigation", + "name": "Automated incident response (AIR) events." + }, + { "value": 65, "Membername": "Quarantine", "name": "Quarantine events." }, + { "value": 66, "Membername": "MicrosoftForms", "name": "Microsoft Forms events." }, + { "value": 67, "Membername": "ApplicationAudit", "name": "Application audit events." }, + { + "value": 68, + "Membername": "ComplianceSupervisionExchange", + "name": "Events tracked by the Communication compliance offensive language model." + }, + { + "value": 69, + "Membername": "CustomerKeyServiceEncryption", + "name": "Events related to the customer key encryption service." + }, + { + "value": 70, + "Membername": "OfficeNative", + "name": "Events related to sensitivity labels applied to Office documents." + }, + { + "value": 71, + "Membername": "MipAutoLabelSharePointItem", + "name": "Auto-labeling events in SharePoint." + }, + { + "value": 72, + "Membername": "MipAutoLabelSharePointPolicyLocation", + "name": "Auto-labeling policy events in SharePoint." + }, + { "value": 73, "Membername": "MicrosoftTeamsShifts", "name": "Teams Shifts events." }, + { + "value": 75, + "Membername": "MipAutoLabelExchangeItem", + "name": "Auto-labeling events in Exchange." + }, + { "value": 76, "Membername": "CortanaBriefing", "name": "Briefing email events." }, + { + "value": 78, + "Membername": "WDATPAlerts", + "name": "Events related to alerts generated by Windows Defender for Endpoint." + }, + { + "value": 82, + "Membername": "SensitivityLabelPolicyMatch", + "name": "Events generated when the file labeled with a sensitivity label is opened or renamed." + }, + { + "value": 83, + "Membername": "SensitivityLabelAction", + "name": "Event generated when sensitivity labels are applied, upStringd, or removed from a file." + }, + { + "value": 84, + "Membername": "SensitivityLabeledFileAction", + "name": "Events generated when a file labeled with a sensitivity label is opened or renamed." + }, + { + "value": 85, + "Membername": "AttackSim", + "name": "Events related to user activities in Attack Simulation & Training in Microsoft Defender for Office 365." + }, + { + "value": 86, + "Membername": "AirManualInvestigation", + "name": "Events related to manual investigations in Automated investigation and response (AIR)." + }, + { + "value": 87, + "Membername": "SecurityComplianceRBAC", + "name": "Security and compliance RBAC events." + }, + { + "value": 88, + "Membername": "UserTraining", + "name": "Events related to user training in Attack Simulation & Training in Microsoft Defender for Office 365." + }, + { + "value": 89, + "Membername": "AirAdminActionInvestigation", + "name": "Events related to admin actions in Automated investigation and response (AIR)." + }, + { + "value": 90, + "Membername": "MSTIC", + "name": "Threat intelligence events in Microsoft Defender for Office 365." + }, + { + "value": 91, + "Membername": "PhysicalBadgingSignal", + "name": "Events related to physical badging signals that support the Insider risk management solution." + }, + { "value": 93, "Membername": "AipDiscover", "name": "AIP scanner events" }, + { + "value": 94, + "Membername": "AipSensitivityLabelAction", + "name": "AIP sensitivity label events" + }, + { "value": 95, "Membername": "AipProtectionAction", "name": "AIP protection events" }, + { "value": 96, "Membername": "AipFileDeleted", "name": "AIP file deletion events" }, + { "value": 97, "Membername": "AipHeartBeat", "name": "AIP heartbeat events" }, + { + "value": 98, + "Membername": "MCASAlerts", + "name": "Events corresponding to alerts triggered by Microsoft Cloud App Security." + }, + { + "value": 99, + "Membername": "OnPremisesFileShareScannerDlp", + "name": "Events related to scanning for sensitive data on file shares." + }, + { + "value": 100, + "Membername": "OnPremisesSharePointScannerDlp", + "name": "Events related to scanning for sensitive data in SharePoint." + }, + { + "value": 101, + "Membername": "ExchangeSearch", + "name": "Events related to using Outlook on the web (OWA) to search for mailbox items." + }, + { + "value": 102, + "Membername": "SharePointSearch", + "name": "Events related to searching an organization's SharePoint home site." + }, + { "value": 103, "Membername": "PrivacyInsights", "name": "Privacy insight events." }, + { "value": 105, "Membername": "MyAnalyticsSettings", "name": "MyAnalytics events." }, + { + "value": 106, + "Membername": "SecurityComplianceUserChange", + "name": "Events related to modifying or deleting a user." + }, + { + "value": 107, + "Membername": "ComplianceDLPExchangeClassification", + "name": "Exchange DLP classification events." + }, + { + "value": 109, + "Membername": "MipExactDataMatch", + "name": "Exact Data Match (EDM) classification events." + }, + { + "value": 113, + "Membername": "MS365DCustomDetection", + "name": "Events related to custom detection actions in Microsoft 365 Defender." + }, + { + "value": 147, + "Membername": "CoreReportingSettings", + "name": "Reports settings events." + }, + { + "value": 148, + "Membername": "ComplianceConnector", + "name": "Events related to importing non-Microsoft data using data connectors in the Microsoft Purview compliance portal." + }, + { + "value": 154, + "Membername": "OMEPortal", + "name": "Encrypted message portal event logs generated by external recipients." + }, + { + "value": 174, + "Membername": "DataShareOperation", + "name": "Events related to sharing of data ingested via SystemSync." + }, + { + "value": 181, + "Membername": "EduDataLakeDownloadOperation", + "name": "Events related to the export of SystemSync ingested data from the lake." + }, + { + "value": 183, + "Membername": "MicrosoftGraphDataConnectOperation", + "name": "Events related to extractions done by Microsoft Graph Data Connect." + }, + { + "value": 186, + "Membername": "PowerPagesSite", + "name": "Activities related to Power Pages site." + }, + { "value": 188, "Membername": "PlannerPlan", "name": "Microsoft Planner plan events." }, + { + "value": 189, + "Membername": "PlannerCopyPlan", + "name": "Microsoft Planner copy plan events." + }, + { "value": 190, "Membername": "PlannerTask", "name": "Microsoft Planner task events." }, + { + "value": 191, + "Membername": "PlannerRoster", + "name": "Microsoft Planner roster and roster membership events." + }, + { + "value": 192, + "Membername": "PlannerPlanList", + "name": "Microsoft Planner plan list events." + }, + { + "value": 193, + "Membername": "PlannerTaskList", + "name": "Microsoft Planner task list events." + }, + { + "value": 194, + "Membername": "PlannerTenantSettings", + "name": "Microsoft Planner tenant settings events." + }, + { + "value": 195, + "Membername": "ProjectForThewebProject", + "name": "Microsoft Project for the web project events." + }, + { + "value": 196, + "Membername": "ProjectForThewebTask", + "name": "Microsoft Project for the web task events." + }, + { + "value": 197, + "Membername": "ProjectForThewebRoadmap", + "name": "Microsoft Project for the web roadmap events." + }, + { + "value": 198, + "Membername": "ProjectForThewebRoadmapItem", + "name": "Microsoft Project for the web roadmap item events." + }, + { + "value": 199, + "Membername": "ProjectForThewebProjectSettings", + "name": "Microsoft Project for the web project tenant settings events." + }, + { + "value": 200, + "Membername": "ProjectForThewebRoadmapSettings", + "name": "Microsoft Project for the web roadmap tenant settings events." + }, + { "value": 216, "Membername": "Viva Goals", "name": "Viva Goals events." }, + { + "value": 217, + "Membername": "MicrosoftGraphDataConnectConsent", + "name": "Events for consent actions performed by tenant admins for Microsoft Graph Data Connect applications." + }, + { + "value": 218, + "Membername": "AttackSimAdmin", + "name": "Events related to admin activities in Attack Simulation & Training in Microsoft Defender for Office 365." + }, + { "value": 230, "Membername": "TeamsUpStrings", "name": "Teams UpStrings App Events." }, + { + "value": 231, + "Membername": "PlannerRosterSensitivityLabel", + "name": "Microsoft Planner roster sensitivity label events." + }, + { + "value": 237, + "Membername": "DefenderExpertsforXDRAdmin", + "name": "Microsoft Defender Experts Administrator action events." + }, + { + "value": 251, + "Membername": "VfamCreatePolicy", + "name": "Viva Access Management policy create events." + }, + { + "value": 252, + "Membername": "VfamUpStringPolicy", + "name": "Viva Access Management policy upString events." + }, + { + "value": 253, + "Membername": "VfamDeletePolicy", + "name": "Viva Access Management policy delete events." + }, + { + "value": 261, + "Membername": "CopilotInteraction", + "name": "Copilot interaction events." + } + ], + "List:countryList": [ + { "value": "AF", "name": "Afghanistan" }, + { "value": "AX", "name": "\u00c5land Islands" }, + { "value": "AL", "name": "Albania" }, + { "value": "DZ", "name": "Algeria" }, + { "value": "AS", "name": "American Samoa" }, + { "value": "AD", "name": "Andorra" }, + { "value": "AO", "name": "Angola" }, + { "value": "AI", "name": "Anguilla" }, + { "value": "AQ", "name": "Antarctica" }, + { "value": "AG", "name": "Antigua and Barbuda" }, + { "value": "AR", "name": "Argentina" }, + { "value": "AM", "name": "Armenia" }, + { "value": "AW", "name": "Aruba" }, + { "value": "AU", "name": "Australia" }, + { "value": "AT", "name": "Austria" }, + { "value": "AZ", "name": "Azerbaijan" }, + { "value": "BS", "name": "Bahamas" }, + { "value": "BH", "name": "Bahrain" }, + { "value": "BD", "name": "Bangladesh" }, + { "value": "BB", "name": "Barbados" }, + { "value": "BY", "name": "Belarus" }, + { "value": "BE", "name": "Belgium" }, + { "value": "BZ", "name": "Belize" }, + { "value": "BJ", "name": "Benin" }, + { "value": "BM", "name": "Bermuda" }, + { "value": "BT", "name": "Bhutan" }, + { "value": "BO", "name": "Bolivia, Plurinational State of" }, + { "value": "BQ", "name": "Bonaire, Sint Eustatius and Saba" }, + { "value": "BA", "name": "Bosnia and Herzegovina" }, + { "value": "BW", "name": "Botswana" }, + { "value": "BV", "name": "Bouvet Island" }, + { "value": "BR", "name": "Brazil" }, + { "value": "IO", "name": "British Indian Ocean Territory" }, + { "value": "BN", "name": "Brunei Darussalam" }, + { "value": "BG", "name": "Bulgaria" }, + { "value": "BF", "name": "Burkina Faso" }, + { "value": "BI", "name": "Burundi" }, + { "value": "KH", "name": "Cambodia" }, + { "value": "CM", "name": "Cameroon" }, + { "value": "CA", "name": "Canada" }, + { "value": "CV", "name": "Cape Verde" }, + { "value": "KY", "name": "Cayman Islands" }, + { "value": "CF", "name": "Central African Republic" }, + { "value": "TD", "name": "Chad" }, + { "value": "CL", "name": "Chile" }, + { "value": "CN", "name": "China" }, + { "value": "CX", "name": "Christmas Island" }, + { "value": "CC", "name": "Cocos (Keeling) Islands" }, + { "value": "CO", "name": "Colombia" }, + { "value": "KM", "name": "Comoros" }, + { "value": "CG", "name": "Congo" }, + { "value": "CD", "name": "Congo, the Democratic Republic of the" }, + { "value": "CK", "name": "Cook Islands" }, + { "value": "CR", "name": "Costa Rica" }, + { "value": "CI", "name": "C\u00f4te d'Ivoire" }, + { "value": "HR", "name": "Croatia" }, + { "value": "CU", "name": "Cuba" }, + { "value": "CW", "name": "Cura\u00e7ao" }, + { "value": "CY", "name": "Cyprus" }, + { "value": "CZ", "name": "Czech Republic" }, + { "value": "DK", "name": "Denmark" }, + { "value": "DJ", "name": "Djibouti" }, + { "value": "DM", "name": "Dominica" }, + { "value": "DO", "name": "Dominican Republic" }, + { "value": "EC", "name": "Ecuador" }, + { "value": "EG", "name": "Egypt" }, + { "value": "SV", "name": "El Salvador" }, + { "value": "GQ", "name": "Equatorial Guinea" }, + { "value": "ER", "name": "Eritrea" }, + { "value": "EE", "name": "Estonia" }, + { "value": "ET", "name": "Ethiopia" }, + { "value": "FK", "name": "Falkland Islands (Malvinas)" }, + { "value": "FO", "name": "Faroe Islands" }, + { "value": "FJ", "name": "Fiji" }, + { "value": "FI", "name": "Finland" }, + { "value": "FR", "name": "France" }, + { "value": "GF", "name": "French Guiana" }, + { "value": "PF", "name": "French Polynesia" }, + { "value": "TF", "name": "French Southern Territories" }, + { "value": "GA", "name": "Gabon" }, + { "value": "GM", "name": "Gambia" }, + { "value": "GE", "name": "Georgia" }, + { "value": "DE", "name": "Germany" }, + { "value": "GH", "name": "Ghana" }, + { "value": "GI", "name": "Gibraltar" }, + { "value": "GR", "name": "Greece" }, + { "value": "GL", "name": "Greenland" }, + { "value": "GD", "name": "Grenada" }, + { "value": "GP", "name": "Guadeloupe" }, + { "value": "GU", "name": "Guam" }, + { "value": "GT", "name": "Guatemala" }, + { "value": "GG", "name": "Guernsey" }, + { "value": "GN", "name": "Guinea" }, + { "value": "GW", "name": "Guinea-Bissau" }, + { "value": "GY", "name": "Guyana" }, + { "value": "HT", "name": "Haiti" }, + { "value": "HM", "name": "Heard Island and McDonald Islands" }, + { "value": "VA", "name": "Holy See (Vatican City State)" }, + { "value": "HN", "name": "Honduras" }, + { "value": "HK", "name": "Hong Kong" }, + { "value": "HU", "name": "Hungary" }, + { "value": "IS", "name": "Iceland" }, + { "value": "IN", "name": "India" }, + { "value": "ID", "name": "Indonesia" }, + { "value": "IR", "name": "Iran, Islamic Republic of" }, + { "value": "IQ", "name": "Iraq" }, + { "value": "IE", "name": "Ireland" }, + { "value": "IM", "name": "Isle of Man" }, + { "value": "IL", "name": "Israel" }, + { "value": "IT", "name": "Italy" }, + { "value": "JM", "name": "Jamaica" }, + { "value": "JP", "name": "Japan" }, + { "value": "JE", "name": "Jersey" }, + { "value": "JO", "name": "Jordan" }, + { "value": "KZ", "name": "Kazakhstan" }, + { "value": "KE", "name": "Kenya" }, + { "value": "KI", "name": "Kiribati" }, + { "value": "KP", "name": "Korea, Democratic People's Republic of" }, + { "value": "KR", "name": "Korea, Republic of" }, + { "value": "KW", "name": "Kuwait" }, + { "value": "KG", "name": "Kyrgyzstan" }, + { "value": "LA", "name": "Lao People's Democratic Republic" }, + { "value": "LV", "name": "Latvia" }, + { "value": "LB", "name": "Lebanon" }, + { "value": "LS", "name": "Lesotho" }, + { "value": "LR", "name": "Liberia" }, + { "value": "LY", "name": "Libya" }, + { "value": "LI", "name": "Liechtenstein" }, + { "value": "LT", "name": "Lithuania" }, + { "value": "LU", "name": "Luxembourg" }, + { "value": "MO", "name": "Macao" }, + { "value": "MK", "name": "Macedonia, the Former Yugoslav Republic of" }, + { "value": "MG", "name": "Madagascar" }, + { "value": "MW", "name": "Malawi" }, + { "value": "MY", "name": "Malaysia" }, + { "value": "MV", "name": "Maldives" }, + { "value": "ML", "name": "Mali" }, + { "value": "MT", "name": "Malta" }, + { "value": "MH", "name": "Marshall Islands" }, + { "value": "MQ", "name": "Martinique" }, + { "value": "MR", "name": "Mauritania" }, + { "value": "MU", "name": "Mauritius" }, + { "value": "YT", "name": "Mayotte" }, + { "value": "MX", "name": "Mexico" }, + { "value": "FM", "name": "Micronesia, Federated States of" }, + { "value": "MD", "name": "Moldova, Republic of" }, + { "value": "MC", "name": "Monaco" }, + { "value": "MN", "name": "Mongolia" }, + { "value": "ME", "name": "Montenegro" }, + { "value": "MS", "name": "Montserrat" }, + { "value": "MA", "name": "Morocco" }, + { "value": "MZ", "name": "Mozambique" }, + { "value": "MM", "name": "Myanmar" }, + { "value": "NA", "name": "Namibia" }, + { "value": "NR", "name": "Nauru" }, + { "value": "NP", "name": "Nepal" }, + { "value": "NL", "name": "Netherlands" }, + { "value": "NC", "name": "New Caledonia" }, + { "value": "NZ", "name": "New Zealand" }, + { "value": "NI", "name": "Nicaragua" }, + { "value": "NE", "name": "Niger" }, + { "value": "NG", "name": "Nigeria" }, + { "value": "NU", "name": "Niue" }, + { "value": "NF", "name": "Norfolk Island" }, + { "value": "MP", "name": "Northern Mariana Islands" }, + { "value": "NO", "name": "Norway" }, + { "value": "OM", "name": "Oman" }, + { "value": "PK", "name": "Pakistan" }, + { "value": "PW", "name": "Palau" }, + { "value": "PS", "name": "Palestine, State of" }, + { "value": "PA", "name": "Panama" }, + { "value": "PG", "name": "Papua New Guinea" }, + { "value": "PY", "name": "Paraguay" }, + { "value": "PE", "name": "Peru" }, + { "value": "PH", "name": "Philippines" }, + { "value": "PN", "name": "Pitcairn" }, + { "value": "PL", "name": "Poland" }, + { "value": "PT", "name": "Portugal" }, + { "value": "PR", "name": "Puerto Rico" }, + { "value": "QA", "name": "Qatar" }, + { "value": "RE", "name": "R\u00e9union" }, + { "value": "RO", "name": "Romania" }, + { "value": "RU", "name": "Russian Federation" }, + { "value": "RW", "name": "Rwanda" }, + { "value": "BL", "name": "Saint Barth\u00e9lemy" }, + { "value": "SH", "name": "Saint Helena, Ascension and Tristan da Cunha" }, + { "value": "KN", "name": "Saint Kitts and Nevis" }, + { "value": "LC", "name": "Saint Lucia" }, + { "value": "MF", "name": "Saint Martin (French part)" }, + { "value": "PM", "name": "Saint Pierre and Miquelon" }, + { "value": "VC", "name": "Saint Vincent and the Grenadines" }, + { "value": "WS", "name": "Samoa" }, + { "value": "SM", "name": "San Marino" }, + { "value": "ST", "name": "Sao Tome and Principe" }, + { "value": "SA", "name": "Saudi Arabia" }, + { "value": "SN", "name": "Senegal" }, + { "value": "RS", "name": "Serbia" }, + { "value": "SC", "name": "Seychelles" }, + { "value": "SL", "name": "Sierra Leone" }, + { "value": "SG", "name": "Singapore" }, + { "value": "SX", "name": "Sint Maarten (Dutch part)" }, + { "value": "SK", "name": "Slovakia" }, + { "value": "SI", "name": "Slovenia" }, + { "value": "SB", "name": "Solomon Islands" }, + { "value": "SO", "name": "Somalia" }, + { "value": "ZA", "name": "South Africa" }, + { "value": "GS", "name": "South Georgia and the South Sandwich Islands" }, + { "value": "SS", "name": "South Sudan" }, + { "value": "ES", "name": "Spain" }, + { "value": "LK", "name": "Sri Lanka" }, + { "value": "SD", "name": "Sudan" }, + { "value": "SR", "name": "Suriname" }, + { "value": "SJ", "name": "Svalbard and Jan Mayen" }, + { "value": "SZ", "name": "Swaziland" }, + { "value": "SE", "name": "Sweden" }, + { "value": "CH", "name": "Switzerland" }, + { "value": "SY", "name": "Syrian Arab Republic" }, + { "value": "TW", "name": "Taiwan, Province of China" }, + { "value": "TJ", "name": "Tajikistan" }, + { "value": "TZ", "name": "Tanzania, United Republic of" }, + { "value": "TH", "name": "Thailand" }, + { "value": "TL", "name": "Timor-Leste" }, + { "value": "TG", "name": "Togo" }, + { "value": "TK", "name": "Tokelau" }, + { "value": "TO", "name": "Tonga" }, + { "value": "TT", "name": "Trinidad and Tobago" }, + { "value": "TN", "name": "Tunisia" }, + { "value": "TR", "name": "Turkey" }, + { "value": "TM", "name": "Turkmenistan" }, + { "value": "TC", "name": "Turks and Caicos Islands" }, + { "value": "TV", "name": "Tuvalu" }, + { "value": "UG", "name": "Uganda" }, + { "value": "UA", "name": "Ukraine" }, + { "value": "AE", "name": "United Arab Emirates" }, + { "value": "GB", "name": "United Kingdom" }, + { "value": "US", "name": "United States" }, + { "value": "UM", "name": "United States Minor Outlying Islands" }, + { "value": "UY", "name": "Uruguay" }, + { "value": "UZ", "name": "Uzbekistan" }, + { "value": "VU", "name": "Vanuatu" }, + { "value": "VE", "name": "Venezuela, Bolivarian Republic of" }, + { "value": "VN", "name": "Viet Nam" }, + { "value": "VG", "name": "Virgin Islands, British" }, + { "value": "VI", "name": "Virgin Islands, U.S." }, + { "value": "WF", "name": "Wallis and Futuna" }, + { "value": "EH", "name": "Western Sahara" }, + { "value": "YE", "name": "Yemen" }, + { "value": "ZM", "name": "Zambia" }, + { "value": "ZW", "name": "Zimbabwe" } + ] +} diff --git a/src/data/AuditLogTemplates.json b/src/data/AuditLogTemplates.json new file mode 100644 index 000000000000..3b90b5eaedba --- /dev/null +++ b/src/data/AuditLogTemplates.json @@ -0,0 +1,317 @@ +[ + { + "value": "New-InboxRule", + "name": "A new Inbox rule is created", + "template": { + "preset": { "value": "New-InboxRule", "label": "A new Inbox rule is created" }, + "logbook": { "value": "Audit.Exchange", "label": "Exchange" }, + "conditions": [ + { + "Property": { "value": "List:Operation", "label": "Operation" }, + "Operator": { "value": "EQ", "label": "Equals to" }, + "Input": { + "value": "new-inboxrule", + "label": "created new inbox rule in outlook web app" + } + } + ] + } + }, + { + "value": "New-InboxRuleRSS", + "name": "A new Inbox rule is created that forwards e-mails to the RSS feeds folder", + "template": { + "preset": { + "value": "New-InboxRuleRSS", + "label": "A new Inbox rule is created that forwards e-mails to the RSS feeds folder" + }, + "logbook": { "value": "Audit.Exchange", "label": "Exchange" }, + "conditions": [ + { + "Property": { "value": "List:Operation", "label": "Operation" }, + "Operator": { "value": "EQ", "label": "Equals to" }, + "Input": { + "value": "new-inboxrule", + "label": "created new inbox rule in outlook web app" + } + }, + { + "Property": { "value": "String", "label": "MoveToFolder" }, + "Operator": { "value": "like", "label": "Like" }, + "Input": { "value": "*RSS*" } + } + ] + } + }, + { + "value": "Set-InboxRule", + "name": "A existing Inbox rule is edited", + "template": { + "preset": { "value": "Set-InboxRule", "label": "A existing Inbox rule is edited" }, + "logbook": { "value": "Audit.Exchange", "label": "Exchange" }, + "conditions": [ + { + "Property": { "value": "List:Operation", "label": "Operation" }, + "Operator": { "value": "EQ", "label": "Equals to" }, + "Input": { + "value": "set-inboxrule", + "label": "Updated inbox rule in outlook web app" + } + } + ] + } + }, + { + "value": "Set-InboxRuleRSS", + "name": "A existing Inbox rule is edited that forwards e-mails to the RSS feeds folder", + "template": { + "preset": { "value": "Set-InboxRuleRSS", "label": "A existing Inbox rule is edited" }, + "logbook": { "value": "Audit.Exchange", "label": "Exchange" }, + "conditions": [ + { + "Property": { "value": "List:Operation", "label": "Operation" }, + "Operator": { "value": "EQ", "label": "Equals to" }, + "Input": { + "value": "set-inboxrule", + "label": "Updated inbox rule in outlook web app" + } + }, + { + "Property": { "value": "String", "label": "MoveToFolder" }, + "Operator": { "value": "like", "label": "Like" }, + "Input": { "value": "*RSS*" } + } + ] + } + }, + { + "value": "Add member to role.", + "name": "A user has been added to an admin role", + "template": { + "preset": { + "value": "Add member to role.", + "label": "A user has been added to an admin role" + }, + "logbook": { "value": "Audit.AzureActiveDirectory", "label": "Azure AD" }, + "conditions": [ + { + "Property": { "value": "List:Operation", "label": "Operation" }, + "Operator": { "value": "EQ", "label": "Equals to" }, + "Input": { + "value": "Add member to role.", + "label": "Add member to role." + } + } + ] + } + }, + { + "value": "Update StsRefreshTokenValidFrom Timestamp.", + "name": "A user sessions have been revoked", + "template": { + "preset": { + "value": "Update StsRefreshTokenValidFrom Timestamp.", + "label": "A user sessions have been revoked" + }, + "logbook": { "value": "Audit.AzureActiveDirectory", "label": "Azure AD" }, + "conditions": [ + { + "Property": { "value": "List:Operation", "label": "Operation" }, + "Operator": { "value": "EQ", "label": "Equals to" }, + "Input": { + "value": "Update StsRefreshTokenValidFrom Timestamp.", + "label": "Update StsRefreshTokenValidFrom Timestamp." + } + } + ] + } + }, + { + "value": "Disable Strong Authentication.", + "name": "A users MFA has been disabled", + "template": { + "preset": { + "value": "Disable Strong Authentication.", + "label": "A users MFA has been disabled" + }, + "logbook": { "value": "Audit.AzureActiveDirectory", "label": "Azure AD" }, + "conditions": [ + { + "Property": { "value": "List:Operation", "label": "Operation" }, + "Operator": { "value": "EQ", "label": "Equals to" }, + "Input": { + "value": "Disable Strong Authentication.", + "label": "Disable Strong Authentication." + } + } + ] + } + }, + { + "value": "Remove Member from a role.", + "name": "A user has been removed from a role", + "template": { + "preset": { + "value": "Remove Member from a role.", + "label": "A user has been removed from a role" + }, + "logbook": { "value": "Audit.AzureActiveDirectory", "label": "Azure AD" }, + "conditions": [ + { + "Property": { "value": "List:Operation", "label": "Operation" }, + "Operator": { "value": "EQ", "label": "Equals to" }, + "Input": { + "value": "Remove Member from a role.", + "label": "Remove Member from a role." + } + } + ] + } + }, + { + "value": "Reset user password.", + "name": "A user password has been reset", + "template": { + "preset": { + "value": "A user password has been reset", + "label": "Reset user password." + }, + "logbook": { "value": "Audit.AzureActiveDirectory", "label": "Azure AD" }, + "conditions": [ + { + "Property": { "value": "List:Operation", "label": "Operation" }, + "Operator": { "value": "EQ", "label": "Equals to" }, + "Input": { + "value": "Reset user password.", + "label": "Reset user password." + } + } + ] + } + }, + { + "value": "UserLoggedInFromUnknownLocation", + "name": "A user has logged in from a location not in the input list", + "template": { + "preset": { + "value": "UserLoggedInFromUnknownLocation", + "label": "A user has logged in from a location not in the input list" + }, + "logbook": { "value": "Audit.AzureActiveDirectory", "label": "Azure AD" }, + "conditions": [ + { + "Property": { "value": "List:Operation", "label": "Operation" }, + "Operator": { "value": "EQ", "label": "Equals to" }, + "Input": { + "value": "UserLoggedIn", + "label": "A user logged in" + } + }, + { + "Property": { "value": "List:countryList", "label": "CIPPGeoLocation", "multi": true }, + "Operator": { "value": "NotIn", "label": "Not In" } + } + ] + } + }, + { + "value": "Add service principal.", + "name": "A service principal has been created", + "template": { + "preset": { + "value": "Add service principal.", + "label": "A service principal has been created" + }, + "logbook": { "value": "Audit.AzureActiveDirectory", "label": "Azure AD" }, + "conditions": [ + { + "Property": { "value": "List:Operation", "label": "Operation" }, + "Operator": { "value": "EQ", "label": "Equals to" }, + "Input": { + "value": "Add service principal.", + "label": "Add service principal." + } + } + ] + } + }, + { + "value": "Remove service principal.", + "name": "A service principal has been removed", + "template": { + "preset": { + "value": "Remove service principal.", + "label": "A service principal has been removed" + }, + "logbook": { "value": "Audit.AzureActiveDirectory", "label": "Azure AD" }, + "conditions": [ + { + "Property": { "value": "List:Operation", "label": "Operation" }, + "Operator": { "value": "EQ", "label": "Equals to" }, + "Input": { + "value": "Remove service principal.", + "label": "Remove service principal." + } + } + ] + } + }, + { + "value": "badRepIP", + "name": "A user has logged in a using a known VPN, Proxy, Or anonymizer", + "template": { + "preset": { + "value": "badRepIP", + "label": "A user has logged in a using a known VPN, Proxy, Or anonymizer" + }, + "logbook": { "value": "Audit.AzureActiveDirectory", "label": "Azure AD" }, + "conditions": [ + { + "Property": { "value": "List:Operation", "label": "Operation" }, + "Operator": { "value": "EQ", "label": "Equals to" }, + "Input": { + "value": "UserLoggedIn", + "label": "A user logged in" + } + }, + { + "Property": { "value": "String", "label": "CIPPBadRepIP" }, + "Operator": { "value": "EQ", "label": "Equals to" }, + "Input": { + "value": "true", + "label": "true" + } + } + ] + } + }, + { + "value": "HostedIP", + "name": "A user has logged in a using a known hosting provider IP", + "template": { + "preset": { + "value": "HostedIP", + "label": "A user has logged in a using a known hosting provider IP" + }, + "logbook": { "value": "Audit.AzureActiveDirectory", "label": "Azure AD" }, + "conditions": [ + { + "Property": { "value": "List:Operation", "label": "Operation" }, + "Operator": { "value": "EQ", "label": "Equals to" }, + "Input": { + "value": "UserLoggedIn", + "label": "A user logged in" + } + }, + { + "Property": { "value": "String", "label": "CIPPHostedIP" }, + "Operator": { "value": "EQ", "label": "Equals to" }, + "Input": { + "value": "true", + "label": "true" + } + } + ] + } + } +] diff --git a/src/data/GDAPRoles.json b/src/data/GDAPRoles.json index adc846cf3028..bf14e31159e5 100644 --- a/src/data/GDAPRoles.json +++ b/src/data/GDAPRoles.json @@ -671,6 +671,22 @@ "Name": "Virtual Visits Administrator", "ObjectId": "e300d9e7-4a2b-4295-9eff-f1c78b36cc98" }, + { + "ExtensionData": {}, + "Description": "Manage and configure all aspects of Microsoft Viva Goals.", + "IsEnabled": true, + "IsSystem": true, + "Name": "Viva Goals Administrator", + "ObjectId": "92b086b3-e367-4ef2-b869-1de128fb986e" + }, + { + "ExtensionData": {}, + "Description": "Can manage all settings for Microsoft Viva Pulse app.", + "IsEnabled": true, + "IsSystem": true, + "Name": "Viva Pulse Administrator", + "ObjectId": "87761b17-1ed2-4af3-9acd-92a150038160" + }, { "ExtensionData": {}, "Description": "Can provision and manage all aspects of Cloud PCs.", diff --git a/src/data/alerts.json b/src/data/alerts.json new file mode 100644 index 000000000000..d6c7215f2d04 --- /dev/null +++ b/src/data/alerts.json @@ -0,0 +1,93 @@ +[ + { + "name": "MFAAlertUsers", + "label": "Alert on users without any form of MFA", + "recommendedRunInterval": "1d" + }, + { + "name": "MFAAdmins", + "label": "Alert on admins without any form of MFA", + "recommendedRunInterval": "1d" + }, + { + "name": "NoCAConfig", + "label": "Alert on tenants without a Conditional Access policy, while having Conditional Access licensing available.", + "recommendedRunInterval": "1d" + }, + { + "name": "AdminPassword", + "label": "Alert on changed admin Passwords", + "recommendedRunInterval": "30m" + }, + { + "name": "QuotaUsed", + "label": "Alert on % mailbox quota used", + "requiresInput": true, + "inputLabel": "Enter quota percentage", + "inputName": "QuotaUsedQuota", + "recommendedRunInterval": "4h" + }, + { + "name": "SharePointQuota", + "label": "Alert on % SharePoint quota used", + "requiresInput": true, + "inputLabel": "Enter quota percentage", + "inputName": "SharePointQuotaQuota", + "recommendedRunInterval": "4h" + }, + { + "name": "ExpiringLicenses", + "label": "Alert on licenses expiring in 30 days", + "recommendedRunInterval": "7d" + }, + { + "name": "NewAppApproval", + "label": "Alert on new apps in the application approval list", + "recommendedRunInterval": "30m" + }, + { + "name": "SecDefaultsUpsell", + "label": "Alert on Security Defaults automatic enablement", + "recommendedRunInterval": "1d" + }, + { + "name": "DefenderStatus", + "label": "Alert if Defender is not running (Tenant must be on-boarded in Lighthouse)", + "recommendedRunInterval": "4h" + }, + { + "name": "DefenderMalware", + "label": "Alert on Defender Malware found (Tenant must be on-boarded in Lighthouse)", + "recommendedRunInterval": "4h" + }, + { + "name": "UnusedLicenses", + "label": "Alert on unused licenses", + "recommendedRunInterval": "1d" + }, + { + "name": "OverusedLicenses", + "label": "Alert on overused licenses", + "recommendedRunInterval": "7d" + }, + { + "name": "AppSecretExpiry", + "label": "Alert on expiring application secrets", + "recommendedRunInterval": "1d" + }, + { + "name": "ApnCertExpiry", + "label": "Alert on expiring APN certificates", + "recommendedRunInterval": "1d" + }, + { + "name": "VppTokenExpiry", + "label": "Alert on expiring VPP tokens", + "recommendedRunInterval": "1d" + }, + { + "name": "DepTokenExpiry", + "label": "Alert on expiring DEP tokens", + "recommendedRunInterval": "1d" + } +] diff --git a/src/data/standards.json b/src/data/standards.json index 2766676a8ce5..92252d5a36ad 100644 --- a/src/data/standards.json +++ b/src/data/standards.json @@ -60,6 +60,57 @@ "remediate": false } }, + { + "name": "standards.Branding", + "cat": "Global Standards", + "tag": ["lowimpact"], + "helpText": "Sets the branding for the tenant. This includes the login page, and the Office 365 portal.", + "addedComponent": [ + { + "type": "input", + "name": "standards.Branding.signInPageText", + "label": "Sign-in page text" + }, + { + "type": "input", + "name": "standards.Branding.usernameHintText", + "label": "Username hint Text" + }, + { + "type": "boolean", + "name": "standards.Branding.hideAccountResetCredentials", + "label": "Hide self-service password reset" + }, + { + "type": "Select", + "label": "Visual Template", + "name": "standards.Branding.layoutTemplateType", + "values": [ + { + "label": "Full-screen background", + "value": "default" + }, + { + "label": "Parial-screen background", + "value": "verticalSplit" + } + ] + }, + { + "type": "boolean", + "name": "standards.Branding.isHeaderShown", + "label": "Show header" + }, + { + "type":"boolean", + "name":"standards.Branding.isFooterShown", + "label":"Show footer" + } + ], + "label": "Set branding for the tenant", + "impact": "Low Impact", + "impactColour": "info" + }, { "name": "standards.EnableCustomerLockbox", "cat": "Global Standards", @@ -487,6 +538,36 @@ "impact": "Low Impact", "impactColour": "info" }, + { + "name": "standards.GlobalQuarantineNotifications", + "cat": "Exchange Standards", + "tag": ["lowimpact"], + "helpText": "Sets the Global Quarantine Notification Interval to the selected value. Determines how often the quarantine notification is sent to users.", + "addedComponent": [ + { + "type": "Select", + "label": "Select value", + "name": "standards.GlobalQuarantineNotifications.NotificationInterval", + "values": [ + { + "label": "4 hours", + "value": "04:00:00" + }, + { + "label": "1 day/Daily", + "value": "1.00:00:00" + }, + { + "label": "7 days/Weekly", + "value": "7.00:00:00" + } + ] + } + ], + "label": "Set Global Quarantine Notification Interval", + "impact": "Low Impact", + "impactColour": "info" + }, { "name": "standards.DisableTNEF", "cat": "Exchange Standards", @@ -1236,7 +1317,7 @@ "helpText": "Sets the default timezone for the tenant. This will be used for all new users and sites.", "addedComponent": [ { - "type": "input", + "type": "TimezoneSelect", "name": "standards.TenantDefaultTimezone.Timezone", "label": "Timezone" } diff --git a/src/data/timezoneList.json b/src/data/timezoneList.json new file mode 100644 index 000000000000..d259bc80e65d --- /dev/null +++ b/src/data/timezoneList.json @@ -0,0 +1,335 @@ +[ + { + "timezone": "(UTC-12:00) International Date Line West" + }, + { + "timezone": "(UTC-11:00) Coordinated Universal Time-11" + }, + { + "timezone": "(UTC-10:00) Hawaii" + }, + { + "timezone": "(UTC-09:00) Alaska" + }, + { + "timezone": "(UTC-08:00) Baja California" + }, + { + "timezone": "(UTC-08:00) Pacific Time (US and Canada)" + }, + { + "timezone": "(UTC-07:00) Arizona" + }, + { + "timezone": "(UTC-07:00) Chihuahua, La Paz, Mazatlan" + }, + { + "timezone": "(UTC-07:00) Mountain Time (US and Canada)" + }, + { + "timezone": "(UTC-06:00) Central America" + }, + { + "timezone": "(UTC-06:00) Central Time (US and Canada)" + }, + { + "timezone": "(UTC-06:00) Guadalajara, Mexico City, Monterrey" + }, + { + "timezone": "(UTC-06:00) Saskatchewan" + }, + { + "timezone": "(UTC-05:00) Bogota, Lima, Quito" + }, + { + "timezone": "(UTC-05:00) Eastern Time (US and Canada)" + }, + { + "timezone": "(UTC-05:00) Indiana (East)" + }, + { + "timezone": "(UTC-04:30) Caracas" + }, + { + "timezone": "(UTC-04:00) Asuncion" + }, + { + "timezone": "(UTC-04:00) Atlantic Time (Canada)" + }, + { + "timezone": "(UTC-04:00) Cuiaba" + }, + { + "timezone": "(UTC-04:00) Georgetown, La Paz, Manaus, San Juan" + }, + { + "timezone": "(UTC-04:00) Santiago" + }, + { + "timezone": "(UTC-03:30) Newfoundland" + }, + { + "timezone": "(UTC-03:00) Brasilia" + }, + { + "timezone": "(UTC-03:00) Buenos Aires" + }, + { + "timezone": "(UTC-03:00) Cayenne, Fortaleza" + }, + { + "timezone": "(UTC-03:00) Greenland" + }, + { + "timezone": "(UTC-03:00) Montevideo" + }, + { + "timezone": "(UTC-03:00) Salvador" + }, + { + "timezone": "(UTC-02:00) Coordinated Universal Time-02" + }, + { + "timezone": "(UTC-02:00) Mid-Atlantic" + }, + { + "timezone": "(UTC-01:00) Azores" + }, + { + "timezone": "(UTC-01:00) Cabo Verde" + }, + { + "timezone": "(UTC) Casablanca" + }, + { + "timezone": "(UTC) Coordinated Universal Time" + }, + { + "timezone": "(UTC) Dublin, Edinburgh, Lisbon, London" + }, + { + "timezone": "(UTC) Monrovia, Reykjavik" + }, + { + "timezone": "(UTC+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna" + }, + { + "timezone": "(UTC+01:00) Belgrade, Bratislava, Budapest, Ljubljana, Prague" + }, + { + "timezone": "(UTC+01:00) Brussels, Copenhagen, Madrid, Paris" + }, + { + "timezone": "(UTC+01:00) Sarajevo, Skopje, Warsaw, Zagreb" + }, + { + "timezone": "(UTC+01:00) West Central Africa" + }, + { + "timezone": "(UTC+01:00) Windhoek" + }, + { + "timezone": "(UTC+02:00) Amman" + }, + { + "timezone": "(UTC+02:00) Athens, Bucharest" + }, + { + "timezone": "(UTC+02:00) Beirut" + }, + { + "timezone": "(UTC+02:00) Cairo" + }, + { + "timezone": "(UTC+02:00) Damascus" + }, + { + "timezone": "(UTC+02:00) Harare, Pretoria" + }, + { + "timezone": "(UTC+02:00) Helsinki, Kyiv, Riga, Sofia, Tallinn, Vilnius" + }, + { + "timezone": "(UTC+02:00) Jerusalem" + }, + { + "timezone": "(UTC+02:00) Minsk (old)" + }, + { + "timezone": "(UTC+02:00) E. Europe" + }, + { + "timezone": "(UTC+02:00) Kaliningrad" + }, + { + "timezone": "(UTC+03:00) Baghdad" + }, + { + "timezone": "(UTC+03:00) Istanbul" + }, + { + "timezone": "(UTC+03:00) Kuwait, Riyadh" + }, + { + "timezone": "(UTC+03:00) Minsk" + }, + { + "timezone": "(UTC+03:00) Moscow, St. Petersburg, Volgograd" + }, + { + "timezone": "(UTC+03:00) Nairobi" + }, + { + "timezone": "(UTC+03:30) Tehran" + }, + { + "timezone": "(UTC+04:00) Abu Dhabi, Muscat" + }, + { + "timezone": "(UTC+04:00) Astrakhan, Ulyanovsk" + }, + { + "timezone": "(UTC+04:00) Baku" + }, + { + "timezone": "(UTC+04:00) Izhevsk, Samara" + }, + { + "timezone": "(UTC+04:00) Port Louis" + }, + { + "timezone": "(UTC+04:00) Tbilisi" + }, + { + "timezone": "(UTC+04:00) Yerevan" + }, + { + "timezone": "(UTC+04:30) Kabul" + }, + { + "timezone": "(UTC+05:00) Ekaterinburg" + }, + { + "timezone": "(UTC+05:00) Islamabad, Karachi" + }, + { + "timezone": "(UTC+05:00) Tashkent" + }, + { + "timezone": "(UTC+05:30) Chennai, Kolkata, Mumbai, New Delhi" + }, + { + "timezone": "(UTC+05:30) Sri Jayawardenepura" + }, + { + "timezone": "(UTC+05:45) Kathmandu" + }, + { + "timezone": "(UTC+06:00) Astana" + }, + { + "timezone": "(UTC+06:00) Dhaka" + }, + { + "timezone": "(UTC+06:00) Omsk" + }, + { + "timezone": "(UTC+06:30) Yangon (Rangoon)" + }, + { + "timezone": "(UTC+07:00) Bangkok, Hanoi, Jakarta" + }, + { + "timezone": "(UTC+07:00) Barnaul, Gorno-Altaysk" + }, + { + "timezone": "(UTC+07:00) Krasnoyarsk" + }, + { + "timezone": "(UTC+07:00) Novosibirsk" + }, + { + "timezone": "(UTC+07:00) Tomsk" + }, + { + "timezone": "(UTC+08:00) Beijing, Chongqing, Hong Kong, Urumqi" + }, + { + "timezone": "(UTC+08:00) Irkutsk" + }, + { + "timezone": "(UTC+08:00) Kuala Lumpur, Singapore" + }, + { + "timezone": "(UTC+08:00) Perth" + }, + { + "timezone": "(UTC+08:00) Taipei" + }, + { + "timezone": "(UTC+08:00) Ulaanbaatar" + }, + { + "timezone": "(UTC+09:00) Osaka, Sapporo, Tokyo" + }, + { + "timezone": "(UTC+09:00) Seoul" + }, + { + "timezone": "(UTC+09:00) Yakutsk" + }, + { + "timezone": "(UTC+09:30) Adelaide" + }, + { + "timezone": "(UTC+09:30) Darwin" + }, + { + "timezone": "(UTC+10:00) Brisbane" + }, + { + "timezone": "(UTC+10:00) Canberra, Melbourne, Sydney" + }, + { + "timezone": "(UTC+10:00) Guam, Port Moresby" + }, + { + "timezone": "(UTC+10:00) Hobart" + }, + { + "timezone": "(UTC+10:00) Magadan" + }, + { + "timezone": "(UTC+10:00) Vladivostok" + }, + { + "timezone": "(UTC+11:00) Chokurdakh" + }, + { + "timezone": "(UTC+11:00) Sakhalin" + }, + { + "timezone": "(UTC+11:00) Solomon Is., New Caledonia" + }, + { + "timezone": "(UTC+12:00) Anadyr, Petropavlovsk-Kamchatsky" + }, + { + "timezone": "(UTC+12:00) Auckland, Wellington" + }, + { + "timezone": "(UTC+12:00) Coordinated Universal Time+12" + }, + { + "timezone": "(UTC+12:00) Fiji" + }, + { + "timezone": "(UTC+12:00) Petropavlovsk-Kamchatsky - Old" + }, + { + "timezone": "(UTC+13:00) Nuku'alofa" + }, + { + "timezone": "(UTC+13:00) Samoa" + } +] diff --git a/src/importsMap.jsx b/src/importsMap.jsx index 741d722bc721..fd44014ae9c7 100644 --- a/src/importsMap.jsx +++ b/src/importsMap.jsx @@ -1,142 +1,146 @@ import React from 'react' export const importsMap = { - "/home": React.lazy(() => import('./views/home/Home')), - "/cipp/logs": React.lazy(() => import('./views/cipp/Logs')), - "/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')), - "/cipp/403": React.lazy(() => import('./views/pages/page403/Page403')), - "/cipp/500": React.lazy(() => import('./views/pages/page500/Page500')), - "/identity/administration/users/add": React.lazy(() => import('./views/identity/administration/AddUser')), - "/identity/administration/users/addbulk": React.lazy(() => import('./views/identity/administration/AddUserBulk')), - "/identity/administration/users/edit": React.lazy(() => import('./views/identity/administration/EditUser')), - "/identity/administration/users/view": React.lazy(() => import('./views/identity/administration/ViewUser')), - "/identity/administration/users/InviteGuest": React.lazy(() => import('./views/identity/administration/InviteGuest')), - "/identity/administration/ViewBec": React.lazy(() => import('./views/identity/administration/ViewBEC')), - "/identity/administration/users": React.lazy(() => import('./views/identity/administration/Users')), - "/identity/administration/devices": React.lazy(() => import('./views/identity/administration/Devices')), - "/identity/administration/groups/add": React.lazy(() => import('./views/identity/administration/AddGroup')), - "/identity/administration/group-templates": React.lazy(() => import('./views/identity/administration/GroupTemplates')), - "/identity/administration/group-add-template": React.lazy(() => import('./views/identity/administration/AddGroupTemplate')), - "/identity/administration/deploy-group-template": React.lazy(() => import('./views/identity/administration/DeployGroupTemplate')), - "/identity/administration/groups/edit": React.lazy(() => import('./views/identity/administration/EditGroup')), - "/identity/administration/groups/view": React.lazy(() => import('./views/identity/administration/ViewGroup')), - "/identity/administration/groups": React.lazy(() => import('./views/identity/administration/Groups')), - "/identity/administration/roles": React.lazy(() => import('./views/identity/administration/Roles')), - "/identity/administration/deleted-items": React.lazy(() => import('./views/identity/administration/Deleted')), - "/teams-share/teams/business-voice": React.lazy(() => import('./views/teams-share/teams/BusinessVoice')), - "/identity/administration/offboarding-wizard": React.lazy(() => import('./views/identity/administration/OffboardingWizard')), - "/endpoint/reports/devices": React.lazy(() => import('./views/endpoint/intune/Devices')), - "/identity/reports/mfa-report": React.lazy(() => import('./views/identity/reports/MFAReport')), - "/identity/reports/inactive-users-report": React.lazy(() => import('./views/identity/reports/InactiveUsers')), - "/identity/reports/Signin-report": React.lazy(() => import('./views/identity/reports/SignIns')), - "/identity/reports/azure-ad-connect-report": React.lazy(() => import('./views/identity/reports/AzureADConnectReport')), - "/tenant/administration/tenants": React.lazy(() => import('./views/tenant/administration/Tenants')), - "/tenant/administration/tenants/edit": React.lazy(() => import('./views/tenant/administration/EditTenant')), - "/tenant/administration/partner-relationships": React.lazy(() => import('./views/tenant/administration/PartnerRelationships')), - "/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/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')), - "/tenant/administration/app-consent-requests": React.lazy(() => import('./views/tenant/administration/ListAppConsentRequests')), - "/tenant/conditional/list-policies": React.lazy(() => import('./views/tenant/conditional/ConditionalAccess')), - "/tenant/conditional/deploy-vacation": React.lazy(() => import('./views/tenant/conditional/DeployVacation')), - "/tenant/conditional/test-policy": React.lazy(() => import('./views/tenant/conditional/TestCAPolicy')), - "/tenant/conditional/list-named-locations": React.lazy(() => import('./views/tenant/conditional/NamedLocations')), - "/tenant/conditional/deploy": React.lazy(() => import('./views/tenant/conditional/DeployCA')), - "/tenant/conditional/deploy-named-location": React.lazy(() => import('./views/tenant/conditional/DeployNamedLocation')), - "/tenant/conditional/list-template": React.lazy(() => import('./views/tenant/conditional/ListCATemplates')), - "/tenant/conditional/add-template": React.lazy(() => import('./views/tenant/conditional/AddCATemplate')), - "/tenant/administration/list-licenses": React.lazy(() => import('./views/tenant/administration/ListLicences')), - "/tenant/administration/application-consent": React.lazy(() => import('./views/tenant/administration/ListOauthApps')), - "/tenant/standards/list-applied-standards": React.lazy(() => import('./views/tenant/standards/ListAppliedStandards')), - "/tenant/standards/bpa-report": React.lazy(() => import('./views/tenant/standards/BestPracticeAnalyser')), - "/tenant/standards/domains-analyser": React.lazy(() => import('./views/tenant/standards/DomainsAnalyser')), - "/tenant/standards/individual-domains": React.lazy(() => import('./views/tenant/standards/IndividualDomain')), - "/tenant/administration/tenantlookup": React.lazy(() => import('./views/tenant/administration/TenantLookup')), - "/tenant/tools/geoiplookup": React.lazy(() => import('./views/tenant/administration/GeoIPLookup')), - "/tenant/tools/bpa-report-builder": React.lazy(() => import('./views/tenant/standards/BPAReportBuilder')), - "/tenant/standards/alert-list": React.lazy(() => import('./views/security/incidents/ListAlerts')), - "/endpoint/applications/list": React.lazy(() => import('./views/endpoint/applications/ApplicationsList')), - "/endpoint/applications/queue": React.lazy(() => import('./views/endpoint/applications/ListApplicationQueue')), - "/endpoint/applications/add-choco-app": React.lazy(() => import('./views/endpoint/applications/ApplicationsAddChocoApp')), - "/endpoint/applications/add-winget-app": React.lazy(() => import('./views/endpoint/applications/ApplicationsAddWinGet')), - "/endpoint/applications/add-office-app": React.lazy(() => import('./views/endpoint/applications/ApplicationsAddOffice')), - "/endpoint/applications/add-rmm-app": React.lazy(() => import('./views/endpoint/applications/ApplicationsAddRMM')), - "/endpoint/autopilot/add-device": React.lazy(() => import('./views/endpoint/autopilot/AutopilotAddDevice')), - "/endpoint/autopilot/add-profile": React.lazy(() => import('./views/endpoint/autopilot/AutopilotAddProfile')), - "/endpoint/autopilot/add-status-page": React.lazy(() => import('./views/endpoint/autopilot/AutopilotAddStatusPage')), - "/endpoint/autopilot/list-devices": React.lazy(() => import('./views/endpoint/autopilot/AutopilotListDevices')), - "/endpoint/autopilot/list-profiles": React.lazy(() => import('./views/endpoint/autopilot/AutopilotListProfiles')), - "/endpoint/autopilot/list-status-pages": React.lazy(() => import('./views/endpoint/autopilot/AutopilotListStatusPages')), - "/endpoint/MEM/list-policies": React.lazy(() => import('./views/endpoint/intune/MEMListPolicies')), - "/endpoint/MEM/list-compliance-policies": React.lazy(() => import('./views/endpoint/intune/MEMListCompliance')), - "/endpoint/MEM/list-appprotection-policies": React.lazy(() => import('./views/endpoint/intune/MEMListAppProtection')), - "/endpoint/MEM/edit-policy": React.lazy(() => import('./views/endpoint/intune/MEMEditPolicy')), - "/endpoint/MEM/ca-policies": React.lazy(() => import('./views/endpoint/intune/MEMCAPolicies')), - "/endpoint/MEM/add-policy": React.lazy(() => import('./views/endpoint/intune/MEMAddPolicy')), - "/endpoint/MEM/add-policy-template": React.lazy(() => import('./views/endpoint/intune/MEMAddPolicyTemplate')), - "/endpoint/MEM/list-templates": React.lazy(() => import('./views/endpoint/intune/MEMListPolicyTemplates')), - "/security/defender/deployment": React.lazy(() => import('./views/security/defender/DeployDefender')), - "/security/defender/list-defender": React.lazy(() => import('./views/security/defender/ListDefender')), - "/security/defender/list-defender-tvm": React.lazy(() => import('./views/security/defender/ListVuln')), - "/teams-share/onedrive/list": React.lazy(() => import('./views/teams-share/onedrive/OneDriveList')), - "/teams-share/sharepoint/list-sharepoint": React.lazy(() => import('./views/teams-share/sharepoint/SharepointList')), - "/teams-share/teams/list-team": React.lazy(() => import('./views/teams-share/teams/TeamsListTeam')), - "/teams-share/teams/view-team-settings": React.lazy(() => import('./views/teams-share/teams/ViewTeamSettings')), - "/teams-share/teams/add-team": React.lazy(() => import('./views/teams-share/teams/TeamsAddTeam')), - "/teams-share/teams/teams-activity": React.lazy(() => import('./views/teams-share/teams/TeamsActivity')), - "/email/administration/contacts": React.lazy(() => import('./views/email-exchange/administration/ContactsList')), - "/email/connectors/list-connectors": React.lazy(() => import('./views/email-exchange/connectors/ConnectorList')), - "/email/connectors/deploy-connector": React.lazy(() => import('./views/email-exchange/connectors/DeployConnector')), - "/email/connectors/add-connector-templates": React.lazy(() => import('./views/email-exchange/connectors/AddConnectorTemplate')), - "/email/connectors/list-connector-templates": React.lazy(() => import('./views/email-exchange/connectors/ListConnectorTemplates')), - "/email/transport/list-rules": React.lazy(() => import('./views/email-exchange/transport/TransportRules')), - "/email/transport/deploy-rules": React.lazy(() => import('./views/email-exchange/transport/DeployTransport')), - "/email/transport/list-templates": React.lazy(() => import('./views/email-exchange/transport/ListTransportTemplates')), - "/email/transport/add-template": React.lazy(() => import('./views/email-exchange/transport/AddTransportTemplate')), - "/email/spamfilter/list-spamfilter": React.lazy(() => import('./views/email-exchange/spamfilter/Spamfilter')), - "/email/spamfilter/deploy": React.lazy(() => import('./views/email-exchange/spamfilter/DeploySpamfilter')), - "/email/spamfilter/list-templates": React.lazy(() => import('./views/email-exchange/spamfilter/ListSpamfilterTemplates')), - "/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/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')), - "/email/administration/add-contact": React.lazy(() => import('./views/email-exchange/administration/AddContact')), - "/email/administration/edit-calendar-permissions": React.lazy(() => import('./views/email-exchange/administration/EditCalendarPermissions')), - "/email/administration/view-mobile-devices": React.lazy(() => import('./views/email-exchange/administration/ViewMobileDevices')), - "/email/administration/edit-contact": React.lazy(() => import('./views/email-exchange/administration/EditContact')), - "/email/administration/mailboxes": React.lazy(() => import('./views/email-exchange/administration/MailboxesList')), - "/email/administration/mailbox-rules": React.lazy(() => import('./views/email-exchange/administration/MailboxRuleList')), - "/email/administration/Quarantine": React.lazy(() => import('./views/email-exchange/administration/QuarantineList')), - "/email/administration/tenant-allow-block-lists": React.lazy(() => import('./views/email-exchange/administration/ListTenantAllowBlockList')), - "/email/reports/mailbox-statistics": React.lazy(() => import('./views/email-exchange/reports/MailboxStatisticsList')), - "/email/reports/SharedMailboxEnabledAccount": React.lazy(() => import('./views/email-exchange/reports/SharedMailboxEnabledAccount')), - "/email/reports/mailbox-cas-settings": React.lazy(() => import('./views/email-exchange/reports/MailboxClientAccessSettingsList')), - "/email/reports/message-trace": React.lazy(() => import('./views/email-exchange/reports/MessageTrace')), - "/cipp/user-settings": React.lazy(() => import('./views/cipp/UserSettings')), - "/email/reports/phishing-policies": React.lazy(() => import('./views/email-exchange/reports/PhishingPoliciesList')), - "/security/incidents/list-alerts": React.lazy(() => import('./views/security/incidents/ListAlerts')), - "/security/incidents/list-incidents": React.lazy(() => import('./views/security/incidents/ListIncidents')), - "/security/reports/list-device-compliance": React.lazy(() => import('./views/security/reports/ListDeviceComplianceReport')), - "/license": React.lazy(() => import('./views/pages/license/License')), - "/cipp/settings": React.lazy(() => import('./views/cipp/app-settings/CIPPSettings')), - "/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-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')), - "/tenant/administration/appapproval": React.lazy(() => import('./views/cipp/AppApproval')), - "/tenant/administration/gdap-status": React.lazy(() => import('./views/tenant/administration/ListGDAPQueue')), - "/tenant/standards/list-standards": React.lazy(() => import('./views/tenant/standards/ListStandards')), - "/tenant/administration/tenant-offboarding-wizard": React.lazy(() => import('./views/tenant/administration/TenantOffboardingWizard')), - "/tenant/administration/tenant-onboarding-wizard": React.lazy(() => import('./views/tenant/administration/TenantOnboardingWizard')), + "/home": React.lazy(() => import('./views/home/Home')), + "/cipp/logs": React.lazy(() => import('./views/cipp/Logs')), + "/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')), + "/cipp/403": React.lazy(() => import('./views/pages/page403/Page403')), + "/cipp/500": React.lazy(() => import('./views/pages/page500/Page500')), + "/identity/administration/users/add": React.lazy(() => import('./views/identity/administration/AddUser')), + "/identity/administration/users/addbulk": React.lazy(() => import('./views/identity/administration/AddUserBulk')), + "/identity/administration/users/edit": React.lazy(() => import('./views/identity/administration/EditUser')), + "/identity/administration/users/view": React.lazy(() => import('./views/identity/administration/ViewUser')), + "/identity/administration/users/InviteGuest": React.lazy(() => import('./views/identity/administration/InviteGuest')), + "/identity/administration/ViewBec": React.lazy(() => import('./views/identity/administration/ViewBEC')), + "/identity/administration/users": React.lazy(() => import('./views/identity/administration/Users')), + "/identity/administration/devices": React.lazy(() => import('./views/identity/administration/Devices')), + "/identity/administration/groups/add": React.lazy(() => import('./views/identity/administration/AddGroup')), + "/identity/administration/group-templates": React.lazy(() => import('./views/identity/administration/GroupTemplates')), + "/identity/administration/group-add-template": React.lazy(() => import('./views/identity/administration/AddGroupTemplate')), + "/identity/administration/deploy-group-template": React.lazy(() => import('./views/identity/administration/DeployGroupTemplate')), + "/identity/administration/groups/edit": React.lazy(() => import('./views/identity/administration/EditGroup')), + "/identity/administration/groups/view": React.lazy(() => import('./views/identity/administration/ViewGroup')), + "/identity/administration/groups": React.lazy(() => import('./views/identity/administration/Groups')), + "/identity/administration/roles": React.lazy(() => import('./views/identity/administration/Roles')), + "/identity/administration/deleted-items": React.lazy(() => import('./views/identity/administration/Deleted')), + "/teams-share/teams/business-voice": React.lazy(() => import('./views/teams-share/teams/BusinessVoice')), + "/identity/administration/offboarding-wizard": React.lazy(() => import('./views/identity/administration/OffboardingWizard')), + "/endpoint/reports/devices": React.lazy(() => import('./views/endpoint/intune/Devices')), + "/identity/reports/mfa-report": React.lazy(() => import('./views/identity/reports/MFAReport')), + "/identity/reports/inactive-users-report": React.lazy(() => import('./views/identity/reports/InactiveUsers')), + "/identity/reports/Signin-report": React.lazy(() => import('./views/identity/reports/SignIns')), + "/identity/reports/azure-ad-connect-report": React.lazy(() => import('./views/identity/reports/AzureADConnectReport')), + "/tenant/administration/tenants": React.lazy(() => import('./views/tenant/administration/Tenants')), + "/tenant/administration/tenants/edit": React.lazy(() => import('./views/tenant/administration/EditTenant')), + "/tenant/administration/partner-relationships": React.lazy(() => import('./views/tenant/administration/PartnerRelationships')), + "/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/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')), + "/tenant/administration/app-consent-requests": React.lazy(() => import('./views/tenant/administration/ListAppConsentRequests')), + "/tenant/conditional/list-policies": React.lazy(() => import('./views/tenant/conditional/ConditionalAccess')), + "/tenant/conditional/deploy-vacation": React.lazy(() => import('./views/tenant/conditional/DeployVacation')), + "/tenant/conditional/test-policy": React.lazy(() => import('./views/tenant/conditional/TestCAPolicy')), + "/tenant/conditional/list-named-locations": React.lazy(() => import('./views/tenant/conditional/NamedLocations')), + "/tenant/conditional/deploy": React.lazy(() => import('./views/tenant/conditional/DeployCA')), + "/tenant/conditional/deploy-named-location": React.lazy(() => import('./views/tenant/conditional/DeployNamedLocation')), + "/tenant/conditional/list-template": React.lazy(() => import('./views/tenant/conditional/ListCATemplates')), + "/tenant/conditional/add-template": React.lazy(() => import('./views/tenant/conditional/AddCATemplate')), + "/tenant/administration/list-licenses": React.lazy(() => import('./views/tenant/administration/ListLicences')), + "/tenant/administration/application-consent": React.lazy(() => import('./views/tenant/administration/ListOauthApps')), + "/tenant/standards/list-applied-standards": React.lazy(() => import('./views/tenant/standards/ListAppliedStandards')), + "/tenant/standards/bpa-report": React.lazy(() => import('./views/tenant/standards/BestPracticeAnalyser')), + "/tenant/standards/domains-analyser": React.lazy(() => import('./views/tenant/standards/DomainsAnalyser')), + "/tenant/standards/individual-domains": React.lazy(() => import('./views/tenant/standards/IndividualDomain')), + "/tenant/administration/tenantlookup": React.lazy(() => import('./views/tenant/administration/TenantLookup')), + "/tenant/tools/geoiplookup": React.lazy(() => import('./views/tenant/administration/GeoIPLookup')), + "/tenant/tools/bpa-report-builder": React.lazy(() => import('./views/tenant/standards/BPAReportBuilder')), + "/tenant/standards/alert-list": React.lazy(() => import('./views/security/incidents/ListAlerts')), + "/endpoint/applications/list": React.lazy(() => import('./views/endpoint/applications/ApplicationsList')), + "/endpoint/applications/queue": React.lazy(() => import('./views/endpoint/applications/ListApplicationQueue')), + "/endpoint/applications/add-choco-app": React.lazy(() => import('./views/endpoint/applications/ApplicationsAddChocoApp')), + "/endpoint/applications/add-winget-app": React.lazy(() => import('./views/endpoint/applications/ApplicationsAddWinGet')), + "/endpoint/applications/add-office-app": React.lazy(() => import('./views/endpoint/applications/ApplicationsAddOffice')), + "/endpoint/applications/add-rmm-app": React.lazy(() => import('./views/endpoint/applications/ApplicationsAddRMM')), + "/endpoint/autopilot/add-device": React.lazy(() => import('./views/endpoint/autopilot/AutopilotAddDevice')), + "/endpoint/autopilot/add-profile": React.lazy(() => import('./views/endpoint/autopilot/AutopilotAddProfile')), + "/endpoint/autopilot/add-status-page": React.lazy(() => import('./views/endpoint/autopilot/AutopilotAddStatusPage')), + "/endpoint/autopilot/list-devices": React.lazy(() => import('./views/endpoint/autopilot/AutopilotListDevices')), + "/endpoint/autopilot/list-profiles": React.lazy(() => import('./views/endpoint/autopilot/AutopilotListProfiles')), + "/endpoint/autopilot/list-status-pages": React.lazy(() => import('./views/endpoint/autopilot/AutopilotListStatusPages')), + "/endpoint/MEM/list-policies": React.lazy(() => import('./views/endpoint/intune/MEMListPolicies')), + "/endpoint/MEM/list-compliance-policies": React.lazy(() => import('./views/endpoint/intune/MEMListCompliance')), + "/endpoint/MEM/list-appprotection-policies": React.lazy(() => import('./views/endpoint/intune/MEMListAppProtection')), + "/endpoint/MEM/edit-policy": React.lazy(() => import('./views/endpoint/intune/MEMEditPolicy')), + "/endpoint/MEM/ca-policies": React.lazy(() => import('./views/endpoint/intune/MEMCAPolicies')), + "/endpoint/MEM/add-policy": React.lazy(() => import('./views/endpoint/intune/MEMAddPolicy')), + "/endpoint/MEM/add-policy-template": React.lazy(() => import('./views/endpoint/intune/MEMAddPolicyTemplate')), + "/endpoint/MEM/list-templates": React.lazy(() => import('./views/endpoint/intune/MEMListPolicyTemplates')), + "/security/defender/deployment": React.lazy(() => import('./views/security/defender/DeployDefender')), + "/security/defender/list-defender": React.lazy(() => import('./views/security/defender/ListDefender')), + "/security/defender/list-defender-tvm": React.lazy(() => import('./views/security/defender/ListVuln')), + "/teams-share/onedrive/list": React.lazy(() => import('./views/teams-share/onedrive/OneDriveList')), + "/teams-share/sharepoint/list-sharepoint": React.lazy(() => import('./views/teams-share/sharepoint/SharepointList')), + "/teams-share/teams/list-team": React.lazy(() => import('./views/teams-share/teams/TeamsListTeam')), + "/teams-share/teams/view-team-settings": React.lazy(() => import('./views/teams-share/teams/ViewTeamSettings')), + "/teams-share/teams/add-team": React.lazy(() => import('./views/teams-share/teams/TeamsAddTeam')), + "/teams-share/teams/teams-activity": React.lazy(() => import('./views/teams-share/teams/TeamsActivity')), + "/email/administration/contacts": React.lazy(() => import('./views/email-exchange/administration/ContactsList')), + "/email/connectors/list-connectors": React.lazy(() => import('./views/email-exchange/connectors/ConnectorList')), + "/email/connectors/deploy-connector": React.lazy(() => import('./views/email-exchange/connectors/DeployConnector')), + "/email/connectors/add-connector-templates": React.lazy(() => import('./views/email-exchange/connectors/AddConnectorTemplate')), + "/email/connectors/list-connector-templates": React.lazy(() => import('./views/email-exchange/connectors/ListConnectorTemplates')), + "/email/transport/list-rules": React.lazy(() => import('./views/email-exchange/transport/TransportRules')), + "/email/transport/deploy-rules": React.lazy(() => import('./views/email-exchange/transport/DeployTransport')), + "/email/transport/list-templates": React.lazy(() => import('./views/email-exchange/transport/ListTransportTemplates')), + "/email/transport/add-template": React.lazy(() => import('./views/email-exchange/transport/AddTransportTemplate')), + "/email/spamfilter/list-spamfilter": React.lazy(() => import('./views/email-exchange/spamfilter/Spamfilter')), + "/email/spamfilter/deploy": React.lazy(() => import('./views/email-exchange/spamfilter/DeploySpamfilter')), + "/email/spamfilter/list-templates": React.lazy(() => import('./views/email-exchange/spamfilter/ListSpamfilterTemplates')), + "/rooms/management/list-rooms": React.lazy(() => import('./views/email-exchange/rooms/ListRooms')), + "/rooms/management/room-lists": React.lazy(() => import('./views/email-exchange/rooms/ListRoomLists')), + "/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/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')), + "/email/administration/add-contact": React.lazy(() => import('./views/email-exchange/administration/AddContact')), + "/email/administration/edit-calendar-permissions": React.lazy(() => import('./views/email-exchange/administration/EditCalendarPermissions')), + "/email/administration/view-mobile-devices": React.lazy(() => import('./views/email-exchange/administration/ViewMobileDevices')), + "/email/administration/edit-contact": React.lazy(() => import('./views/email-exchange/administration/EditContact')), + "/email/administration/mailboxes": React.lazy(() => import('./views/email-exchange/administration/MailboxesList')), + "/email/administration/mailbox-rules": React.lazy(() => import('./views/email-exchange/administration/MailboxRuleList')), + "/email/administration/Quarantine": React.lazy(() => import('./views/email-exchange/administration/QuarantineList')), + "/email/administration/tenant-allow-block-lists": React.lazy(() => import('./views/email-exchange/administration/ListTenantAllowBlockList')), + "/email/administration/add-tenant-allow-block-list": React.lazy(() => import('./views/email-exchange/administration/AddTenantAllowBlockList')), + "/email/reports/mailbox-statistics": React.lazy(() => import('./views/email-exchange/reports/MailboxStatisticsList')), + "/email/reports/SharedMailboxEnabledAccount": React.lazy(() => import('./views/email-exchange/reports/SharedMailboxEnabledAccount')), + "/email/reports/mailbox-cas-settings": React.lazy(() => import('./views/email-exchange/reports/MailboxClientAccessSettingsList')), + "/email/reports/message-trace": React.lazy(() => import('./views/email-exchange/reports/MessageTrace')), + "/cipp/user-settings": React.lazy(() => import('./views/cipp/UserSettings')), + "/email/reports/phishing-policies": React.lazy(() => import('./views/email-exchange/reports/PhishingPoliciesList')), + "/security/incidents/list-alerts": React.lazy(() => import('./views/security/incidents/ListAlerts')), + "/security/incidents/list-incidents": React.lazy(() => import('./views/security/incidents/ListIncidents')), + "/security/reports/list-device-compliance": React.lazy(() => import('./views/security/reports/ListDeviceComplianceReport')), + "/license": React.lazy(() => import('./views/pages/license/License')), + "/cipp/settings": React.lazy(() => import('./views/cipp/app-settings/CIPPSettings')), + "/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-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')), + "/tenant/administration/appapproval": React.lazy(() => import('./views/cipp/AppApproval')), + "/tenant/administration/gdap-status": React.lazy(() => import('./views/tenant/administration/ListGDAPQueue')), + "/tenant/standards/list-standards": React.lazy(() => import('./views/tenant/standards/ListStandards')), + "/tenant/administration/tenant-offboarding-wizard": React.lazy(() => import('./views/tenant/administration/TenantOffboardingWizard')), + "/tenant/administration/tenant-onboarding": React.lazy(() => import('./views/tenant/administration/TenantOnboarding')), + "/tenant/administration/tenant-onboarding-wizard": React.lazy(() => import('./views/tenant/administration/TenantOnboardingWizard')), } -export default importsMap +export default importsMap \ No newline at end of file diff --git a/src/layout/DefaultLayout.jsx b/src/layout/DefaultLayout.jsx index 0b08e4ad8b8c..23bbc527b527 100644 --- a/src/layout/DefaultLayout.jsx +++ b/src/layout/DefaultLayout.jsx @@ -23,7 +23,7 @@ const DefaultLayout = () => { useEffect(() => { let route = routes.find((route) => route.path.toLowerCase() === location.pathname.toLowerCase()) if (route?.name) { - console.log(route) + //console.log(route) setTitle(route.name) } }, [setTitle, location.pathname]) diff --git a/src/routes.json b/src/routes.json index 4c9a675a515d..e681555e0346 100644 --- a/src/routes.json +++ b/src/routes.json @@ -690,6 +690,18 @@ "component": "views/email-exchange/spamfilter/ListSpamfilterTemplates", "allowedRoles": ["admin", "editor", "readonly"] }, + { + "path": "/rooms/management/list-rooms", + "name": "Rooms", + "component": "views/email-exchange/rooms/ListRooms", + "allowedRoles": ["admin", "editor", "readonly"] + }, + { + "path": "/rooms/management/room-lists", + "name": "Room Lists", + "component": "views/email-exchange/rooms/ListRoomLists", + "allowedRoles": ["admin", "editor", "readonly"] + }, { "path": "/email/tools/mailbox-restore-wizard", "name": "Mailbox Restore Wizard", @@ -774,6 +786,12 @@ "component": "views/email-exchange/administration/ListTenantAllowBlockList", "allowedRoles": ["admin", "editor", "readonly"] }, + { + "name": "Add Tenant Allow/Block List", + "path": "/email/administration/add-tenant-allow-block-list", + "component": "views/email-exchange/administration/AddTenantAllowBlockList", + "allowedRoles": ["admin", "editor", "readonly"] + }, { "name": "Email Reports", "path": "/email/reports", @@ -936,6 +954,12 @@ "component": "views/tenant/administration/TenantOffboardingWizard", "allowedRoles": ["admin"] }, + { + "path": "/tenant/administration/tenant-onboarding", + "name": "Tenant Onboarding", + "component": "views/tenant/administration/TenantOnboarding", + "allowedRoles": ["admin"] + }, { "path": "/tenant/administration/tenant-onboarding-wizard", "name": "Tenant Onboarding", diff --git a/src/views/cipp/Setup.jsx b/src/views/cipp/Setup.jsx index c0a728ff8ec7..bf09e8b04baf 100644 --- a/src/views/cipp/Setup.jsx +++ b/src/views/cipp/Setup.jsx @@ -6,7 +6,12 @@ import { faCheck, faExclamationTriangle } from '@fortawesome/free-solid-svg-icon import { CippWizard } from 'src/components/layout' import PropTypes from 'prop-types' import { Condition, RFFCFormInput, RFFCFormRadio } from 'src/components/forms' -import { useLazyGenericGetRequestQuery, useLazyGenericPostRequestQuery } from 'src/store/api/app' +import { + useLazyExecPermissionsAccessCheckQuery, + useLazyGenericGetRequestQuery, + useLazyGenericPostRequestQuery, +} from 'src/store/api/app' +import { Link } from 'react-router-dom' function useInterval(callback, delay, state) { const savedCallback = useRef() @@ -49,11 +54,7 @@ Error.propTypes = { } const Setup = () => { - const [setupDone, setSetupdone] = useState(false) - const valbutton = (value) => - getResults.data?.step < 5 - ? undefined - : `You do not have to click next. Finish the wizard via the setup button below. After it says "Setup Completed" you may browse away from this page.` + const [checkPermissions, permissionsResult] = useLazyExecPermissionsAccessCheckQuery() const [genericPostRequest, postResults] = useLazyGenericPostRequestQuery() const [genericGetRequest, getResults] = useLazyGenericGetRequestQuery() const onSubmit = (values) => { @@ -72,7 +73,6 @@ const Setup = () => { path: 'api/ExecSAMSetup', params: { CreateSAM: true, partnersetup: true }, }) - setSetupdone(false) } useInterval( @@ -82,19 +82,35 @@ const Setup = () => { path: 'api/ExecSAMSetup', params: { CheckSetupProcess: true, step: getResults.data?.step }, }) - } else { - setSetupdone(true) } }, 10000, getResults.data, ) const formValues = {} + + const stepsDetails = [ + { id: 1, text: 'Step 1 - First Login' }, + { id: 2, text: 'Step 2 - Creating Application & Approving Application' }, + { id: 3, text: 'Step 3 - Receiving Token' }, + { id: 4, text: 'Step 4 - Finishing Authentication Setup' }, + ] + const RenderSteps = ({ currentStep = 0 }) => ( + <> + {currentStep > 0 && + stepsDetails.slice(0, currentStep - 1).map((step) => ( +
+ {step.text} - Completed +
+ ))} + + ) return (
@@ -112,7 +128,7 @@ const Setup = () => { rel="noreferrer" target="_blank" > - here. + here @@ -134,7 +150,11 @@ const Setup = () => {
- +

Step 2

Enter the secure application model credentials.
@@ -144,32 +164,49 @@ const Setup = () => { Click the buttons below to refresh your token. -
Remember to login under a account that has been added to the correct GDAP - groups and the group 'AdminAgents'. +
Remember to login under a service account that has been added to the correct + GDAP groups and the group 'AdminAgents'.
- {getResults.isUninitialized && genericGetRequest({ path: 'api/ExecListAppId' })} - {getResults.isSuccess && ( - <> - - - - Refresh Graph Token - - - - - - )}
+ {getResults.isUninitialized && genericGetRequest({ path: 'api/ExecListAppId' })} + {getResults.isSuccess && ( + <> + + + + + Refresh Graph Token + + + + + + + )}

- When clicking the button below, the setup wizard starts. This is a 5 step process. - Please use a Global Administrator to perform these tasks. You can restart the process - at any time, by clicking on the start button once more. + Click the button below to start the setup wizard. You will need the following + prerequisites: +

  • + A CIPP Service Account. For more information on how to create a service account + click{' '} + + here + +
  • +
  • (Temporary) Global Administrator permissions for the CIPP Service Account
  • +
  • + Multi-factor authentication enabled for the CIPP Service Account, with no trusted + locations or other exclusions. +
  • { className="btn btn-primary" type="button" onClick={() => startCIPPSetup(true)} - validate={() => valbutton()} > - Start Setup Wizard + {getResults.isFetching && } Start Setup Wizard - -
    - {getResults.isFetching && Loading} {getResults.isSuccess && ( <> - {getResults.data?.step < 5 ? ( + + {getResults.data?.step < 5 && getResults.data?.step > 0 && ( - ) : ( - )} - Step {getResults.data?.step} - {getResults.data.message}{' '} - {getResults.data.url && ( - - HERE - + {getResults.data?.step > 0 && getResults.data?.step < 5 && ( + <> + Step {getResults.data?.step} - {getResults.data.message}{' '} + {getResults.data.url && ( + + HERE + + )} + )} )} + {getResults.data?.step === 5 && ( +

    + {permissionsResult.isFetching && } Authentication has been received. + Checking if all prerequisites are met to connect to your tenants. + {permissionsResult.isUninitialized && checkPermissions()} +

    + )} + + {permissionsResult.data?.Results && ( + <> + + + {permissionsResult.data.Results?.Messages && ( + <> + {permissionsResult.data.Results?.Messages?.map((m, idx) => ( +
    {m}
    + ))} + + )} +
    +
    + + {permissionsResult.data.Results?.ErrorMessages?.length >= 1 && ( + + {permissionsResult.data.Results?.ErrorMessages?.map((m, idx) => ( +
    {m}
    + ))} +
    + )} +
    + + )} +
    @@ -251,40 +320,29 @@ const Setup = () => { /> + + +
    + Submit info +
    +
    +
    + {postResults.isFetching && ( + + Loading + + )} + {postResults.isSuccess && {postResults.data.Results}}
    -
    - - -
    -

    Step 3

    -
    Confirm and apply
    -
    -
    - {!postResults.isSuccess && ( - - {(props) => { - return ( - <> - - - - {usedWizard && - 'You have used the setup wizard. You can close this screen. Setup has been completed.'} - {!usedWizard && - 'You are sending your own Secure Application Model setup to the Keyvault. For security reasons we do not show the keys. Please make sure you have entered the keys correctly.'} - - - - ) - }} - - )} - {postResults.isFetching && ( - - Loading - - )} - {postResults.isSuccess && {postResults.data.Results}} + + {(props) => { + if (props.values.SetupType === 'ExistingSAM') { + setNoSubmit(false) + } else { + setNoSubmit(true) + } + }} +
    diff --git a/src/views/cipp/app-settings/SettingsExtensionMappings.jsx b/src/views/cipp/app-settings/SettingsExtensionMappings.jsx index 8f8ebed14efc..de3246422499 100644 --- a/src/views/cipp/app-settings/SettingsExtensionMappings.jsx +++ b/src/views/cipp/app-settings/SettingsExtensionMappings.jsx @@ -1,12 +1,22 @@ import { useLazyGenericGetRequestQuery, useLazyGenericPostRequestQuery } from 'src/store/api/app.js' -import { CButton, CCallout, CCardText, CCol, CForm, CRow, CSpinner, CTooltip } from '@coreui/react' +import { + CAccordion, + CButton, + CCallout, + CCardText, + CCol, + CForm, + CRow, + CSpinner, + CTooltip, +} from '@coreui/react' import { Form } from 'react-final-form' import { RFFSelectSearch } from 'src/components/forms/index.js' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { faCircleNotch } from '@fortawesome/free-solid-svg-icons' import React, { useEffect } from 'react' import { CippCallout } from 'src/components/layout/index.js' -import CippButtonCard from 'src/components/contentcards/CippButtonCard' +import CippAccordionItem from 'src/components/contentcards/CippAccordionItem' import { CippTable } from 'src/components/tables' import { CellTip } from 'src/components/tables/CellGenericFormat' @@ -41,6 +51,9 @@ export function SettingsExtensionMappings() { setHaloExtensionconfig({ path: 'api/ExecExtensionMapping?AddMapping=Halo', values: { mappings: originalFormat }, + }).then(() => { + listHaloBackend({ path: 'api/ExecExtensionMapping?List=Halo' }) + setMappingValue({}) }) } const onNinjaOrgsSubmit = () => { @@ -52,6 +65,9 @@ export function SettingsExtensionMappings() { setNinjaOrgsExtensionconfig({ path: 'api/ExecExtensionMapping?AddMapping=NinjaOrgs', values: { mappings: originalFormat }, + }).then(() => { + listNinjaOrgsBackend({ path: 'api/ExecExtensionMapping?List=NinjaOrgs' }) + setMappingValue({}) }) } @@ -68,7 +84,6 @@ export function SettingsExtensionMappings() { const onNinjaFieldsSubmit = (values) => { setNinjaFieldsExtensionconfig({ path: 'api/ExecExtensionMapping?AddMapping=NinjaFields', - values: { mappings: values }, }) } @@ -91,8 +106,11 @@ export function SettingsExtensionMappings() { }, //filter out any undefined values ).filter((item) => item !== undefined) - setHaloMappingsArray((currentHaloMappings) => [...currentHaloMappings, ...newMappings]) - + setHaloMappingsArray((currentHaloMappings) => [...currentHaloMappings, ...newMappings]).then( + () => { + listHaloBackend({ path: 'api/ExecExtensionMapping?List=Halo' }) + }, + ) setHaloAutoMap(true) } @@ -230,82 +248,99 @@ export function SettingsExtensionMappings() { listNinjaOrgsBackend({ path: 'api/ExecExtensionMapping?List=NinjaOrgs' })} {listBackendNinjaFieldsResult.isUninitialized && listNinjaFieldsBackend({ path: 'api/ExecExtensionMapping?List=NinjaFields' })} - <> - - - - {extensionHaloConfigResult.isFetching && ( - - )} - Save Mappings - - onHaloAutomap()} className="me-2"> - {extensionNinjaOrgsAutomapResult.isFetching && ( - - )} - Automap HaloPSA Clients - - - } - > - {listBackendHaloResult.isFetching ? ( - - ) : ( -
    { - return ( - - - Use the table below to map your client to the correct PSA client. - { - //load all the existing mappings and show them first in a table. - listBackendHaloResult.isSuccess && ( - - ) - } - - - ({ - name: tenant.displayName, - value: tenant.customerId, - }))} - onChange={(e) => { - setMappingArray(e.value) - }} - /> - - - - - - ({ - name: client.name, - value: client.value, - }))} - onChange={(e) => setMappingValue(e)} - placeholder="Select a HaloPSA Client" - /> - - + + + + {extensionHaloConfigResult.isFetching && ( + + )} + Save Mappings + + onHaloAutomap()} className="me-2"> + {extensionNinjaOrgsAutomapResult.isFetching && ( + + )} + Automap HaloPSA Clients + + + } + > + {listBackendHaloResult.isFetching && listBackendHaloResult.isUninitialized ? ( + + ) : ( + { + return ( + + + Use the table below to map your client to the correct PSA client. + { + //load all the existing mappings and show them first in a table. + listBackendHaloResult.isSuccess && ( + + ) + } + + + { + return !Object.keys(listBackendHaloResult.data?.Mappings).includes( + tenant.customerId, + ) + }).map((tenant) => ({ + name: tenant.displayName, + value: tenant.customerId, + }))} + onChange={(e) => { + setMappingArray(e.value) + }} + isLoading={listBackendHaloResult.isFetching} + /> + + + + + + { + return !Object.values(listBackendHaloResult.data?.Mappings) + .map((value) => { + return value.value + }) + .includes(client.value) + }).map((client) => ({ + name: client.name, + value: client.value, + }))} + onChange={(e) => setMappingValue(e)} + placeholder="Select a HaloPSA Client" + isLoading={listBackendHaloResult.isFetching} + /> + + { + if ( + mappingValue.value !== undefined && + Object.values(haloMappingsArray) + .map((item) => item.haloId) + .includes(mappingValue.value) === false + ) { //set the new mapping in the array setHaloMappingsArray([ ...haloMappingsArray, @@ -318,123 +353,138 @@ export function SettingsExtensionMappings() { }, ]) } - className={`my-4 circular-button`} - title={'+'} + }} + className={`my-4 circular-button`} + title={'+'} + > + + + + + + {HaloAutoMap && ( + + Automapping has been executed. Remember to check the changes and save + them. + + )} + {(extensionHaloConfigResult.isSuccess || extensionHaloConfigResult.isError) && + !extensionHaloConfigResult.isFetching && ( + - - - - - - {HaloAutoMap && ( - - Automapping has been executed. Remember to check the changes and save - them. - + {extensionHaloConfigResult.isSuccess + ? extensionHaloConfigResult.data.Results + : 'Error'} + )} - {(extensionHaloConfigResult.isSuccess || - extensionHaloConfigResult.isError) && - !extensionHaloConfigResult.isFetching && ( - - {extensionHaloConfigResult.isSuccess - ? extensionHaloConfigResult.data.Results - : 'Error'} - - )} - - - - After editing the mappings you must click Save Mappings for the changes to - take effect. The table will be saved exactly as presented. - - - ) - }} - /> - )} - - - - {' '} - - - {extensionNinjaOrgsConfigResult.isFetching && ( - - )} - Set Mappings - - onNinjaOrgsAutomap()} className="me-2"> - {extensionNinjaOrgsAutomapResult.isFetching && ( - - )} - Automap NinjaOne Organizations - - - } - > - {listBackendNinjaOrgsResult.isFetching ? ( - - ) : ( - { - return ( - - - Use the table below to map your client to the correct NinjaOne Organization. - { - //load all the existing mappings and show them first in a table. - listBackendNinjaOrgsResult.isSuccess && ( - - ) - } - - - ({ - name: tenant.displayName, - value: tenant.customerId, - }))} - onChange={(e) => { - setMappingArray(e.value) - }} - /> - - - - - - ({ - name: client.name, - value: client.value, - }))} - onChange={(e) => setMappingValue(e)} - placeholder="Select a NinjaOne Organization" - /> - - - //set the new mapping in the array + + + + After editing the mappings you must click Save Mappings for the changes to + take effect. The table will be saved exactly as presented. + + + ) + }} + /> + )} + + + + {extensionNinjaOrgsConfigResult.isFetching && ( + + )} + Set Mappings + + onNinjaOrgsAutomap()} className="me-2"> + {extensionNinjaOrgsAutomapResult.isFetching && ( + + )} + Automap NinjaOne Organizations + + + } + > + {listBackendNinjaOrgsResult.isFetching && listBackendNinjaOrgsResult.isUninitialized ? ( + + ) : ( + { + return ( + + + Use the table below to map your client to the correct NinjaOne Organization. + { + //load all the existing mappings and show them first in a table. + listBackendNinjaOrgsResult.isSuccess && ( + + ) + } + + + { + return !Object.keys( + listBackendNinjaOrgsResult.data?.Mappings, + ).includes(tenant.customerId) + }).map((tenant) => ({ + name: tenant.displayName, + value: tenant.customerId, + }))} + onChange={(e) => { + setMappingArray(e.value) + }} + isLoading={listBackendNinjaOrgsResult.isFetching} + /> + + + + + + { + return !Object.values(listBackendNinjaOrgsResult.data?.Mappings) + .map((value) => { + return value.value + }) + .includes(client.value.toString()) + }).map((client) => ({ + name: client.name, + value: client.value, + }))} + onChange={(e) => setMappingValue(e)} + placeholder="Select a NinjaOne Organization" + isLoading={listBackendNinjaOrgsResult.isFetching} + /> + + { + //set the new mapping in the array + if ( + mappingValue.value !== undefined && + Object.values(ninjaMappingsArray) + .map((item) => item.ninjaId) + .includes(mappingValue.value) === false + ) { setNinjaMappingsArray([ ...ninjaMappingsArray, { @@ -446,144 +496,137 @@ export function SettingsExtensionMappings() { }, ]) } - className={`my-4 circular-button`} - title={'+'} + }} + className={`my-4 circular-button`} + title={'+'} + > + + + + + + {(extensionNinjaOrgsAutomapResult.isSuccess || + extensionNinjaOrgsAutomapResult.isError) && + !extensionNinjaOrgsAutomapResult.isFetching && ( + - - - - - - {(extensionNinjaOrgsAutomapResult.isSuccess || - extensionNinjaOrgsAutomapResult.isError) && - !extensionNinjaOrgsAutomapResult.isFetching && ( - - {extensionNinjaOrgsAutomapResult.isSuccess - ? extensionNinjaOrgsAutomapResult.data.Results - : 'Error'} - - )} - {(extensionNinjaOrgsConfigResult.isSuccess || - extensionNinjaOrgsConfigResult.isError) && - !extensionNinjaOrgsConfigResult.isFetching && ( - - {extensionNinjaOrgsConfigResult.isSuccess - ? extensionNinjaOrgsConfigResult.data.Results - : 'Error'} - - )} - - - - After editing the mappings you must click Save Mappings for the changes to - take effect. The table will be saved exactly as presented. - - - ) - }} - /> - )} - - - - - {extensionNinjaFieldsConfigResult.isFetching && ( - - )} - Set Mappings - - } - > - {listBackendNinjaFieldsResult.isFetching ? ( - - ) : ( - { - return ( - - -
    Organization Global Custom Field Mapping
    -

    - Use the table below to map your Organization Field to the correct NinjaOne - Field -

    - {listBackendNinjaFieldsResult.isSuccess && - listBackendNinjaFieldsResult.data.CIPPOrgFields.map((CIPPOrgFields) => ( - item.type === CIPPOrgFields.Type || item.type === 'unset', - )} - placeholder="Select a Field" - /> - ))} -
    - -
    Device Custom Field Mapping
    -

    - Use the table below to map your Device field to the correct NinjaOne - WYSIWYG Field -

    - {listBackendNinjaFieldsResult.isSuccess && - listBackendNinjaFieldsResult.data.CIPPNodeFields.map((CIPPNodeFields) => ( - - item.type === CIPPNodeFields.Type || item.type === 'unset', - )} - placeholder="Select a Field" - /> - ))} -
    - - {(extensionNinjaFieldsConfigResult.isSuccess || - extensionNinjaFieldsConfigResult.isError) && - !extensionNinjaFieldsConfigResult.isFetching && ( - - {extensionNinjaFieldsConfigResult.isSuccess - ? extensionNinjaFieldsConfigResult.data.Results - : 'Error'} - - )} - -
    - ) - }} - /> - )} -
    -
    - + {extensionNinjaOrgsAutomapResult.isSuccess + ? extensionNinjaOrgsAutomapResult.data.Results + : 'Error'} + + )} + {(extensionNinjaOrgsConfigResult.isSuccess || + extensionNinjaOrgsConfigResult.isError) && + !extensionNinjaOrgsConfigResult.isFetching && ( + + {extensionNinjaOrgsConfigResult.isSuccess + ? extensionNinjaOrgsConfigResult.data.Results + : 'Error'} + + )} + + + + After editing the mappings you must click Save Mappings for the changes to + take effect. The table will be saved exactly as presented. + + + ) + }} + /> + )} +
    + + {extensionNinjaFieldsConfigResult.isFetching && ( + + )} + Set Mappings + + } + > + {listBackendNinjaFieldsResult.isFetching ? ( + + ) : ( + { + return ( + + +
    Organization Global Custom Field Mapping
    +

    + Use the table below to map your Organization Field to the correct NinjaOne + Field +

    + {listBackendNinjaFieldsResult.isSuccess && + listBackendNinjaFieldsResult.data.CIPPOrgFields.map((CIPPOrgFields) => ( + item.type === CIPPOrgFields.Type || item.type === 'unset', + )} + placeholder="Select a Field" + /> + ))} +
    + +
    Device Custom Field Mapping
    +

    + Use the table below to map your Device field to the correct NinjaOne WYSIWYG + Field +

    + {listBackendNinjaFieldsResult.isSuccess && + listBackendNinjaFieldsResult.data.CIPPNodeFields.map((CIPPNodeFields) => ( + item.type === CIPPNodeFields.Type || item.type === 'unset', + )} + placeholder="Select a Field" + /> + ))} +
    + + {(extensionNinjaFieldsConfigResult.isSuccess || + extensionNinjaFieldsConfigResult.isError) && + !extensionNinjaFieldsConfigResult.isFetching && ( + + {extensionNinjaFieldsConfigResult.isSuccess + ? extensionNinjaFieldsConfigResult.data.Results + : 'Error'} + + )} + +
    + ) + }} + /> + )} +
    + ) } diff --git a/src/views/cipp/app-settings/SettingsExtensions.jsx b/src/views/cipp/app-settings/SettingsExtensions.jsx index fc728407ded3..20ebbcee9725 100644 --- a/src/views/cipp/app-settings/SettingsExtensions.jsx +++ b/src/views/cipp/app-settings/SettingsExtensions.jsx @@ -1,6 +1,7 @@ import { useLazyGenericGetRequestQuery, useLazyGenericPostRequestQuery } from 'src/store/api/app.js' import React, { useRef } from 'react' import { + CAccordion, CAlert, CButton, CCallout, @@ -21,6 +22,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { faCircleNotch } from '@fortawesome/free-solid-svg-icons' import { CippCallout } from 'src/components/layout/index.js' import CippButtonCard from 'src/components/contentcards/CippButtonCard' +import CippAccordionItem from 'src/components/contentcards/CippAccordionItem' /** * Executes various operations related to settings and extensions. @@ -105,65 +107,64 @@ export function SettingsExtensions() { {extensionConfigResult.data.Results} )} - + {Extensions.map((integration, idx) => ( - - -

    {integration.helpText}

    - { - return ( - - - - {integration.SettingOptions.map( - (integrationOptions, idx) => - integrationOptions.type === 'input' && ( - - - - ), - )} - {integration.SettingOptions.map( - (integrationOptions, idx) => - integrationOptions.type === 'checkbox' && ( - - - - ), - )} - - - - - ) - }} - /> -
    -
    + +

    {integration.helpText}

    + { + return ( + + + + {integration.SettingOptions.map( + (integrationOptions, idx) => + integrationOptions.type === 'input' && ( + + + + ), + )} + {integration.SettingOptions.map( + (integrationOptions, idx) => + integrationOptions.type === 'checkbox' && ( + + + + ), + )} + + + + + ) + }} + /> +
    ))} -
    +
    ) diff --git a/src/views/cipp/app-settings/SettingsGeneral.jsx b/src/views/cipp/app-settings/SettingsGeneral.jsx index 98a34210c72b..04446add8ceb 100644 --- a/src/views/cipp/app-settings/SettingsGeneral.jsx +++ b/src/views/cipp/app-settings/SettingsGeneral.jsx @@ -20,6 +20,7 @@ import { CListGroup, CListGroupItem, CRow, + CTable, } from '@coreui/react' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { faCircleNotch } from '@fortawesome/free-solid-svg-icons' @@ -29,6 +30,7 @@ import { CippTable } from 'src/components/tables/index.js' import { TenantSelectorMultiple } from 'src/components/utilities/index.js' import { SettingsGeneralRow } from 'src/views/cipp/app-settings/components/SettingsGeneralRow.jsx' import CippButtonCard from 'src/components/contentcards/CippButtonCard' +import { ListGroupContentCard } from 'src/components/contentcards' /** * SettingsGeneral component. @@ -139,72 +141,6 @@ export function SettingsGeneral() { checkAccess({ tenantDomains: AllTenantSelector }) } - function getTokenOffcanvasProps({ tokenResults }) { - let tokenDetails = tokenResults.AccessTokenDetails - let helpLinks = tokenResults.Links - let tokenOffcanvasGroups = [] - if (tokenDetails?.Name !== '') { - let tokenItems = [] - let tokenOffcanvasGroup = {} - tokenItems.push({ - heading: 'User', - content: tokenDetails?.Name, - }) - tokenItems.push({ - heading: 'UPN', - content: tokenDetails?.UserPrincipalName, - }) - tokenItems.push({ - heading: 'App Registration', - content: ( - - {tokenDetails?.AppName} - - ), - }) - tokenItems.push({ - heading: 'IP Address', - content: tokenDetails?.IPAddress, - }) - tokenItems.push({ - heading: 'Auth Methods', - content: tokenDetails?.AuthMethods.join(', '), - }) - tokenItems.push({ - heading: 'Tenant ID', - content: tokenDetails?.TenantId, - }) - tokenOffcanvasGroup.items = tokenItems - tokenOffcanvasGroup.title = 'Claims' - tokenOffcanvasGroups.push(tokenOffcanvasGroup) - } - - if (helpLinks.length > 0) { - let linkItems = [] - let linkItemGroup = {} - helpLinks.map((link, idx) => - linkItems.push({ - heading: '', - content: ( - - {link.Text} - - ), - }), - ) - linkItemGroup.title = 'Help Links' - linkItemGroup.items = linkItems - if (linkItemGroup.items.length > 0) { - tokenOffcanvasGroups.push(linkItemGroup) - } - } - - return tokenOffcanvasGroups - } - const tableProps = { pagination: false, actions: [ @@ -276,50 +212,80 @@ export function SettingsGeneral() { <> {permissionsResult.data.Results?.AccessTokenDetails?.Name !== '' && ( <> - setTokenOffcanvasVisible(true)}> - Details - - setTokenOffcanvasVisible(false)} - /> + + + + Authentication User + Authentication IP + Application + + + + + + {permissionsResult.data.Results?.AccessTokenDetails?.UserPrincipalName} + + {permissionsResult.data.Results?.AccessTokenDetails?.IPAddress} + + + Link + + + + + )} - - {permissionsResult.data.Results?.Messages && ( - <> - {permissionsResult.data.Results?.Messages?.map((m, idx) => ( -
    {m}
    + + + + {permissionsResult.data.Results?.Messages && ( + <> + {permissionsResult.data.Results?.Messages?.map((m, idx) => ( +
    {m}
    + ))} + + )} +
    +
    + + {permissionsResult.data.Results?.ErrorMessages?.length > 0 || + (permissionsResult.data.Results?.MissingPermissions.length > 0 && ( + + <> + {permissionsResult.data.Results?.ErrorMessages?.map((m, idx) => ( +
    {m}
    + ))} + + {permissionsResult.data.Results?.MissingPermissions.length > 0 && ( + <> + Your Secure Application Model is missing the following permissions. + See the documentation on how to add permissions{' '} + + here + + . + + {permissionsResult.data.Results?.MissingPermissions?.map( + (r, index) => ( + {r} + ), + )} + + + )} +
    ))} - - )} - {permissionsResult.data.Results?.MissingPermissions.length > 0 && ( - <> - Your Secure Application Model is missing the following permissions. See the - documentation on how to add permissions{' '} - - here - - . - - {permissionsResult.data.Results?.MissingPermissions?.map((r, index) => ( - {r} - ))} - - - )} -
    + + )} diff --git a/src/views/cipp/app-settings/SettingsMaintenance.jsx b/src/views/cipp/app-settings/SettingsMaintenance.jsx index d7387f849400..a65add447d5d 100644 --- a/src/views/cipp/app-settings/SettingsMaintenance.jsx +++ b/src/views/cipp/app-settings/SettingsMaintenance.jsx @@ -1,22 +1,13 @@ import React, { useState } from 'react' -import { useLazyGenericGetRequestQuery } from 'src/store/api/app.js' -import { - CButton, - CCard, - CCardBody, - CCardHeader, - CCardTitle, - CCol, - CForm, - CRow, -} from '@coreui/react' -import { Form } from 'react-final-form' -import Skeleton from 'react-loading-skeleton' -import { RFFCFormSelect } from 'src/components/forms/index.js' +import { useGenericGetRequestQuery, useLazyGenericGetRequestQuery } from 'src/store/api/app.js' +import { CButton, CCallout, CCol, CRow, CSpinner } from '@coreui/react' +import CippChartCard from 'src/components/contentcards/CippChartCard' +import { CippDatatable, CippTable, cellDateFormatter } from 'src/components/tables' +import { cellGenericFormatter } from 'src/components/tables/CellGenericFormat' +import { CippCallout, CippContentCard } from 'src/components/layout' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' -import { faLink, faScroll } from '@fortawesome/free-solid-svg-icons' -import { CippCodeBlock } from 'src/components/utilities/index.js' -import { Buffer } from 'buffer' +import CippButtonCard from 'src/components/contentcards/CippButtonCard' +import { CippActionsOffcanvas, ModalService } from 'src/components/utilities' /** * Performs maintenance operations on settings. @@ -24,138 +15,232 @@ import { Buffer } from 'buffer' * @returns {JSX.Element} The JSX element representing the settings maintenance component. */ export function SettingsMaintenance() { - const [selectedScript, setSelectedScript] = useState() - const [listBackend, listBackendResult] = useLazyGenericGetRequestQuery() - const [listScript, listScriptResult] = useLazyGenericGetRequestQuery() - const [listScriptLink, listScriptLinkResult] = useLazyGenericGetRequestQuery() + const orchestrators = useGenericGetRequestQuery({ + path: '/api/ExecDurableFunctions', + params: { Action: 'ListOrchestrators' }, + }) + const durableStats = useGenericGetRequestQuery({ + path: '/api/ExecDurableFunctions', + params: { Action: 'ListStats' }, + }) - const handleSubmit = async (values) => { - listScript({ path: 'api/ExecMaintenanceScripts', params: values }) - setSelectedScript(values.ScriptFile) - } + const [resetDurables, resetDurableStatus] = useLazyGenericGetRequestQuery() - const handleGetLink = () => { - listScriptLink({ - path: 'api/ExecMaintenanceScripts', - params: { ScriptFile: selectedScript, MakeLink: 'True' }, + const handleResetDurables = (action) => { + var actionText = '' + if (action === 'ResetDurables') { + actionText = 'clear Durable Queues? This will stop all queued functions from executing.' + } else if (action === 'PurgeOrchestrators') { + actionText = + 'purge Orchestrator Instances and History? This will also remove the largemessages blob container.' + } + ModalService.confirm({ + title: 'Danger Zone', + body:
    Are you sure you want to {actionText}
    , + onConfirm: () => + resetDurables({ + path: '/api/ExecDurableFunctions', + params: { Action: action }, + }).then(() => { + orchestrators.refetch() + durableStats.refetch() + }), + confirmLabel: 'Reset', + cancelLabel: 'Cancel', }) } - return ( + + const Actions = (row, rowIndex, formatExtraData) => { + const [ocVisible, setOCVisible] = useState(false) + const [getOrchestratorHistory, orchestratorHistory] = useLazyGenericGetRequestQuery() + + function loadOffCanvasDetails(id) { + setOCVisible(true) + getOrchestratorHistory({ + path: 'api/ExecDurableFunctions', + params: { Action: 'ListOrchestratorHistory', PartitionKey: id }, + }) + } + var actions = [ + { + label: 'View History', + color: 'info', + modal: true, + modalType: 'table', + modalBody: orchestratorHistory?.data?.Results ? orchestratorHistory?.data?.Results : '', + }, + { + label: 'Purge Orchestrator', + color: 'danger', + modal: true, + icon: , + modalUrl: `/api/ExecDurableFunctions?Action=PurgeOrchestrators&PartitionKey=${row.PartitionKey}`, + modalMessage: + 'Are you sure you want to purge this orchestrator instance and related history?', + }, + ] + + return ( + <> + loadOffCanvasDetails(row.PartitionKey)}> + + + setOCVisible(false)} + /> + + ) + } + + const ResetButton = ( <> - {listBackendResult.isUninitialized && listBackend({ path: 'api/ExecMaintenanceScripts' })} - - - - - Maintenance - - - { - return ( - - {listBackendResult.isFetching && ( - <> - - - - - - - )} - {!listBackendResult.isFetching && listBackendResult.isSuccess && ( - <> - - - - - - - - - - Load Script - - - - - )} - - ) - }} - /> - - + handleResetDurables('ResetDurables')} + color="danger" + className="me-2" + > + Clear Durable Queues + + handleResetDurables('PurgeOrchestrators')} + color="danger" + className="me-2" + > + Purge Orchestrators + + + ) + + return ( +
    + + + { + return queue?.Name + })} + ChartData={durableStats.data?.Queues?.map((queue) => { + return queue?.ApproximateMessageCount + })} + isFetching={durableStats.isFetching} + refreshFunction={() => durableStats.refetch()} + /> + + + { + return status.Name + })} + ChartData={durableStats?.data?.Orchestrators?.map((status) => { + return status.Count + })} + isFetching={durableStats.isFetching} + refreshFunction={() => durableStats.refetch()} + /> + + + + +

    Use these actions when troubleshooting performance issues with the backend.

    +

    + NOTE: Resetting durables will terminate any running processes. +

    +
    + + {resetDurableStatus.isFetching && } + {!resetDurableStatus.isFetching && resetDurableStatus.isSuccess && ( + + {resetDurableStatus?.data?.Message} + + )} +
    - - - {listScriptResult.isFetching && ( - - - - - - )} - {!listScriptResult.isFetching && listScriptResult.isSuccess && ( - - - Script Details - - -

    - - - Create Link - -

    - {listScriptLinkResult.isSuccess && ( -

    - {listScriptLinkResult.data.Link !== undefined && ( - <> -

    - Copy this text into a PowerShell terminal, we recommend Azure Cloud Shell. - Azure modules and the az command line utilties are required for these - scripts to work. The link is valid for 5 minutes. -

    - - - )} -

    - )} - {listScriptResult.data.ScriptContent !== undefined && ( -

    -

    Maintenance Script Contents
    - -

    - )} -
    -
    - )} + + + + , + modalUrl: `/api/ExecDurableFunctions?Action=PurgeOrchestrators&PartitionKey=!PartitionKey`, + modalMessage: + 'Are you sure you want to purge the selected orchestrator instances and related history?', + }, + ], + }} + columns={[ + { + name: 'Created', + selector: (row) => row['CreatedTime'], + sortable: true, + exportSelector: 'CreatedTime', + cell: cellDateFormatter({ format: 'short' }), + }, + { + name: 'Completed', + selector: (row) => row?.CompletedTime, + sortable: true, + exportSelector: 'CompletedTime', + cell: cellDateFormatter({ format: 'short' }), + }, + { + name: 'Name', + selector: (row) => row['Name'], + sortable: true, + exportSelector: 'Name', + cell: cellGenericFormatter(), + }, + { + name: 'Status', + selector: (row) => row['RuntimeStatus'], + sortable: true, + exportSelector: 'RuntimeStatus', + cell: cellGenericFormatter(), + }, + { + name: 'Input', + selector: (row) => row['Input'], + cell: cellGenericFormatter(), + }, + { + name: 'Actions', + cell: Actions, + maxWidth: '100px', + }, + ]} + filterlist={[ + { filterName: 'Running', filter: 'Complex: RuntimeStatus eq Running' }, + { filterName: 'Pending', filter: 'Complex: RuntimeStatus eq Pending' }, + { filterName: 'Completed', filter: 'Complex: RuntimeStatus eq Completed' }, + { filterName: 'Failed', filter: 'Complex: RuntimeStatus eq Failed' }, + ]} + isFetching={orchestrators.isFetching} + refreshFunction={() => orchestrators.refetch()} + /> + - +
    ) } diff --git a/src/views/cipp/app-settings/SettingsNotifications.jsx b/src/views/cipp/app-settings/SettingsNotifications.jsx index 555a1fbdd19d..781d2ac72e6a 100644 --- a/src/views/cipp/app-settings/SettingsNotifications.jsx +++ b/src/views/cipp/app-settings/SettingsNotifications.jsx @@ -44,11 +44,20 @@ export function SettingsNotifications() { generateTestAlert({ text: 'Test Alert', Severity: 'Alert' })} + onClick={() => + generateTestAlert({ text: 'Manually Generated Test Alert', Severity: 'Alert' }) + } disabled={generateAlertResult.isFetching} > - {generateAlertResult.isFetching && } - {generateAlertResult.isSuccess && } + {generateAlertResult.isFetching ? ( + + ) : ( + <> + {generateAlertResult.isSuccess && ( + + )} + + )} Generate Test Alert diff --git a/src/views/cipp/app-settings/SettingsPartner.jsx b/src/views/cipp/app-settings/SettingsPartner.jsx index 388e2a6a6e9d..b569b9c33c24 100644 --- a/src/views/cipp/app-settings/SettingsPartner.jsx +++ b/src/views/cipp/app-settings/SettingsPartner.jsx @@ -19,7 +19,7 @@ import { CSpinner, } from '@coreui/react' import { Form } from 'react-final-form' -import { RFFSelectSearch } from 'src/components/forms/index.js' +import { RFFCFormSwitch, RFFSelectSearch } from 'src/components/forms/index.js' import React, { useEffect } from 'react' import { CippCallout } from 'src/components/layout/index.js' import { CippCodeBlock } from 'src/components/utilities' @@ -45,6 +45,7 @@ export function SettingsPartner() { const onSubmit = (values) => { const shippedValues = { EventType: values?.EventType?.map((event) => event.value), + standardsExcludeAllTenants: values?.standardsExcludeAllTenants, } submitWebhook({ path: '/api/ExecPartnerWebhook?Action=CreateSubscription', @@ -141,6 +142,8 @@ export function SettingsPartner() { label: event, value: event, })), + standardsExcludeAllTenants: + webhookConfig?.data?.Results?.standardsExcludeAllTenants, }} render={({ handleSubmit }) => ( <> @@ -156,6 +159,14 @@ export function SettingsPartner() { refreshFunction={() => webhookEvents.refetch()} helpText="Select the events you want to receive notifications for." /> + { + const tenantDomain = useSelector((state) => state.app.currentTenant.defaultDomainName) + + const [genericPostRequest, postResults] = useLazyGenericPostRequestQuery() + const onSubmit = (values) => { + const shippedValues = { + tenantID: tenantDomain, + entries: values.entries, + listType: values.listType, + notes: values.notes, + listMethod: values.listMethod, + NoExpiration: values.NoExpiration, + } + genericPostRequest({ path: '/api/AddTenantAllowBlockList', values: shippedValues }) + } + return ( + + + + Add Tenant Allow/Block List + + + { + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + Add Entry + + + + {postResults.isFetching && ( + + + + )} + {postResults.isSuccess && ( + {postResults.data.Results} + )} + + ) + }} + /> + + + + ) +} + +export default AddTenantAllowBlockList diff --git a/src/views/email-exchange/administration/ListTenantAllowBlockList.jsx b/src/views/email-exchange/administration/ListTenantAllowBlockList.jsx index 51d6d7ffe50b..e3a44ab7e7cc 100644 --- a/src/views/email-exchange/administration/ListTenantAllowBlockList.jsx +++ b/src/views/email-exchange/administration/ListTenantAllowBlockList.jsx @@ -1,92 +1,121 @@ -import React from 'react' +import React, { useState } from 'react' import { useSelector } from 'react-redux' import { CButton } from '@coreui/react' -import { faEdit } from '@fortawesome/free-solid-svg-icons' -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' -import { Link } from 'react-router-dom' import { CippPageList } from 'src/components/layout' -import { CellTip } from 'src/components/tables' +import { CellTip, cellBooleanFormatter } from 'src/components/tables' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { faEdit, faEllipsisV } from '@fortawesome/free-solid-svg-icons' +import { TitleButton } from 'src/components/buttons' +import { CippActionsOffcanvas } from 'src/components/utilities' -const AllowBlockList = () => { +const Actions = (row, rowIndex, formatExtraData) => { const tenant = useSelector((state) => state.app.currentTenant) - const Actions = (row, rowIndex, formatExtraData) => ( + const [ocVisible, setOCVisible] = useState(false) + return ( <> - - - - - - - - - - + setOCVisible(true)}> + + + setOCVisible(false)} + /> ) +} + +const columns = [ + { + name: 'Value', + selector: (row) => row['Value'], + sortable: true, + cell: (row) => CellTip(row['Value']), + exportSelector: 'Value', + }, + { + name: 'ListType', + selector: (row) => row['ListType'], + sortable: true, + cell: (row) => CellTip(row['ListType']), + exportSelector: 'ListType', + maxWidth: '80px', + }, + { + name: 'Action', + selector: (row) => row['Action'], + sortable: true, + cell: (row) => CellTip(row['Action']), + exportSelector: 'Action', + maxWidth: '80px', + }, + { + name: 'Notes', + selector: (row) => row['Notes'], + sortable: true, + cell: (row) => CellTip(row['Notes']), + exportSelector: 'Notes', + }, + { + name: 'LastModifiedDateTime', + selector: (row) => row['LastModifiedDateTime'], + sortable: true, + cell: (row) => CellTip(row['LastModifiedDateTime']), + exportSelector: 'LastModifiedDateTime', + }, + { + name: 'ExpirationDate', + selector: (row) => row['ExpirationDate'], + sortable: true, + cell: (row) => CellTip(row['ExpirationDate']), + exportSelector: 'ExpirationDate', + }, + /*{ + name: 'Actions', + cell: Actions, + maxWidth: '80px', + },*/ +] - const columns = [ - { - name: 'Value', - selector: (row) => row['Value'], - sortable: true, - cell: (row) => CellTip(row['Value']), - exportSelector: 'Value', - }, - { - name: 'ListType', - selector: (row) => row['ListType'], - sortable: true, - cell: (row) => CellTip(row['ListType']), - exportSelector: 'ListType', - maxWidth: '80px', - }, - { - name: 'Action', - selector: (row) => row['Action'], - sortable: true, - cell: (row) => CellTip(row['Action']), - exportSelector: 'Action', - maxWidth: '80px', - }, - { - name: 'Notes', - selector: (row) => row['Notes'], - sortable: true, - cell: (row) => CellTip(row['Notes']), - exportSelector: 'Notes', - }, - { - name: 'LastModifiedDateTime', - selector: (row) => row['LastModifiedDateTime'], - sortable: true, - cell: (row) => CellTip(row['LastModifiedDateTime']), - exportSelector: 'LastModifiedDateTime', - }, - { - name: 'ExpirationDate', - selector: (row) => row['ExpirationDate'], - sortable: true, - cell: (row) => CellTip(row['ExpirationDate']), - exportSelector: 'ExpirationDate', - }, - // { - // name: 'Actions', - // cell: Actions, - // maxWidth: '80px', - // }, - ] +const AllowBlockList = () => { + const tenant = useSelector((state) => state.app.currentTenant) return ( + } title="Tenant Allow/Block Lists" datatable={{ - columns, - path: '/api/ListTenantAllowBlockList', + keyField: 'id', reportName: `${tenant?.defaultDomainName}-TenantAllowBlockList`, + path: '/api/ListTenantAllowBlockList', + columns, params: { TenantFilter: tenant?.defaultDomainName }, }} /> diff --git a/src/views/email-exchange/administration/QuarantineList.jsx b/src/views/email-exchange/administration/QuarantineList.jsx index a97211466431..7453af66b1a7 100644 --- a/src/views/email-exchange/administration/QuarantineList.jsx +++ b/src/views/email-exchange/administration/QuarantineList.jsx @@ -98,7 +98,7 @@ const QuarantineList = () => { name: 'Reason', sortable: true, exportSelector: 'Type', - maxWidth: '150px', + maxWidth: '200px', }, { selector: (row) => row['ReceivedTime'], @@ -126,7 +126,7 @@ const QuarantineList = () => { { name: 'Actions', cell: Offcanvas, - maxWidth: '150px', + maxWidth: '100px', }, ] @@ -139,6 +139,33 @@ const QuarantineList = () => { reportName: `${tenant?.defaultDomainName}-Mailbox-Quarantine`, path: '/api/ListMailQuarantine', columns, + tableProps: { + selectableRows: true, + actionsList: [ + { + label: 'Release', + color: 'info', + modal: true, + modalUrl: `/api/ExecQuarantineManagement?TenantFilter=${tenant.defaultDomainName}&ID=!Identity&Type=Release`, + modalMessage: 'Are you sure you want to release these messages?', + }, + { + label: 'Deny', + color: 'info', + modal: true, + modalUrl: `/api/ExecQuarantineManagement?TenantFilter=${tenant.defaultDomainName}&ID=!Identity&Type=Deny`, + modalMessage: 'Are you sure you want to deny these messages?', + }, + { + label: 'Release & Allow Sender', + color: 'info', + modal: true, + modalUrl: `/api/ExecQuarantineManagement?TenantFilter=${tenant.defaultDomainName}&ID=!Identity&Type=Release&AllowSender=true`, + modalMessage: + 'Are you sure you want to release these messages, and add the senders to the whitelist?', + }, + ], + }, params: { TenantFilter: tenant?.defaultDomainName }, }} /> diff --git a/src/views/email-exchange/connectors/ConnectorList.jsx b/src/views/email-exchange/connectors/ConnectorList.jsx index 990e1d555199..c18d5eeb0004 100644 --- a/src/views/email-exchange/connectors/ConnectorList.jsx +++ b/src/views/email-exchange/connectors/ConnectorList.jsx @@ -151,6 +151,20 @@ const ConnectorList = () => { } tenantSelector={true} datatable={{ + filterlist: [ + { filterName: 'Enabled connectors', filter: 'Complex: Enabled eq true' }, + { filterName: 'Disabled connectors', filter: 'Complex: Enabled eq false' }, + { filterName: 'Inbound connectors', filter: 'Complex: cippconnectortype eq inbound' }, + { filterName: 'Outbound connectors', filter: 'Complex: cippconnectortype eq outbound' }, + { + filterName: 'Transport rule connectors', + filter: 'Complex: IsTransportRuleScoped eq true', + }, + { + filterName: 'Non-transport rule connectors', + filter: 'Complex: IsTransportRuleScoped eq false', + }, + ], reportName: `${tenant?.defaultDomainName}-connectors-list`, path: '/api/ListExchangeConnectors', params: { TenantFilter: tenant?.defaultDomainName }, diff --git a/src/views/email-exchange/rooms/ListRoomLists.jsx b/src/views/email-exchange/rooms/ListRoomLists.jsx new file mode 100644 index 000000000000..3da41e5c6fd3 --- /dev/null +++ b/src/views/email-exchange/rooms/ListRoomLists.jsx @@ -0,0 +1,76 @@ +import React from 'react' +import { useSelector } from 'react-redux' +import { CButton } from '@coreui/react' +import { faEye, faEdit } from '@fortawesome/free-solid-svg-icons' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { Link } from 'react-router-dom' +import { CippPageList } from 'src/components/layout' +import { CellTip } from 'src/components/tables' + +const RoomLists = () => { + const tenant = useSelector((state) => state.app.currentTenant) + /* const Actions = (row, rowIndex, formatExtraData) => ( + <> + + + + + + + )*/ + + const columns = [ + { + name: 'Name', + selector: (row) => row['displayName'], + sortable: true, + cell: (row) => CellTip(row['displayName']), + exportSelector: 'displayName', + maxWidth: '500px', + }, + { + name: 'Coordinates', + selector: (row) => row['geoCoordinates'], + sortable: true, + exportSelector: 'geoCoordinates', + maxWidth: '200px', + }, + { + name: 'PlaceId', + selector: (row) => row['placeId'], + sortable: true, + cell: (row) => CellTip(row['placeId']), + exportSelector: 'placeId', + maxWidth: '300px', + }, + /*{ + name: 'Address', + selector: (row) => row['address'], + sortable: true, + cell: (row) => CellTip(row['address']), + exportSelector: 'address', + maxWidth: '200px', + },*/ + /*{ + name: 'Actions', + cell: Actions, + maxWidth: '80px', + },*/ + ] + + return ( + + ) +} + +export default RoomLists diff --git a/src/views/email-exchange/rooms/ListRooms.jsx b/src/views/email-exchange/rooms/ListRooms.jsx new file mode 100644 index 000000000000..a94a60fb8b12 --- /dev/null +++ b/src/views/email-exchange/rooms/ListRooms.jsx @@ -0,0 +1,91 @@ +import React from 'react' +import { useSelector } from 'react-redux' +import { CButton } from '@coreui/react' +import { faEye, faEdit } from '@fortawesome/free-solid-svg-icons' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { Link } from 'react-router-dom' +import { CippPageList } from 'src/components/layout' +import { CellTip } from 'src/components/tables' + +const Rooms = () => { + const tenant = useSelector((state) => state.app.currentTenant) + /* const Actions = (row, rowIndex, formatExtraData) => ( + <> + + + + + + + )*/ + + const columns = [ + { + name: 'Name', + selector: (row) => row['displayName'], + sortable: true, + cell: (row) => CellTip(row['displayName']), + exportSelector: 'displayName', + }, + { + name: 'Building', + selector: (row) => row['building'], + sortable: true, + exportSelector: 'building', + maxWidth: '200px', + }, + { + name: 'Floor', + selector: (row) => row['floorNumber'], + sortable: true, + cell: (row) => CellTip(row['floorNumber']), + exportSelector: 'floorNumber', + maxWidth: '100px', + }, + { + name: 'Capacity', + selector: (row) => row['capacity'], + sortable: true, + cell: (row) => CellTip(row['capacity']), + exportSelector: 'capacity', + maxWidth: '100px', + }, + { + name: 'bookingType', + selector: (row) => row['bookingType'], + sortable: true, + cell: (row) => CellTip(row['bookingType']), + exportSelector: 'bookingType', + maxWidth: '200px', + }, + /*{ + name: 'Address', + selector: (row) => row['address'], + sortable: true, + cell: (row) => CellTip(row['address']), + exportSelector: 'address', + maxWidth: '100px', + },*/ + /*{ + name: 'Actions', + cell: Actions, + maxWidth: '80px', + },*/ + ] + + return ( + + ) +} + +export default Rooms diff --git a/src/views/email-exchange/tools/MailTest.jsx b/src/views/email-exchange/tools/MailTest.jsx index 633d351fd489..27b55070f6db 100644 --- a/src/views/email-exchange/tools/MailTest.jsx +++ b/src/views/email-exchange/tools/MailTest.jsx @@ -40,22 +40,22 @@ const MailTest = () => { }, { name: 'SPF', - selector: (row) => row?.AuthResult.filter((x) => x?.Name === 'spf')[0]?.Status == 'pass', + selector: (row) => row?.AuthResult?.find((x) => x?.Name === 'spf')?.Status == 'pass', cell: cellBooleanFormatter(), }, { name: 'DKIM', - selector: (row) => row?.AuthResult.filter((x) => x?.Name === 'dkim')[0]?.Status == 'pass', + selector: (row) => row?.AuthResult?.find((x) => x?.Name === 'dkim')?.Status == 'pass', cell: cellBooleanFormatter(), }, { name: 'DMARC', - selector: (row) => row?.AuthResult.filter((x) => x?.Name === 'dmarc')[0]?.Status == 'pass', + selector: (row) => row?.AuthResult?.find((x) => x?.Name === 'dmarc')?.Status == 'pass', cell: cellBooleanFormatter(), }, { name: 'Comp Auth', - selector: (row) => row?.AuthResult.filter((x) => x?.Name === 'compauth')[0]?.Status == 'pass', + selector: (row) => row?.AuthResult?.find((x) => x?.Name === 'compauth')?.Status == 'pass', cell: cellBooleanFormatter(), }, { diff --git a/src/views/email-exchange/transport/TransportRules.jsx b/src/views/email-exchange/transport/TransportRules.jsx index 4e6f3f68a35d..9d534e17dee5 100644 --- a/src/views/email-exchange/transport/TransportRules.jsx +++ b/src/views/email-exchange/transport/TransportRules.jsx @@ -131,6 +131,10 @@ const TransportRulesList = () => { } datatable={{ + filterlist: [ + { filterName: 'Enabled rules', filter: 'Complex: State eq Enabled' }, + { filterName: 'Disabled rules', filter: 'Complex: State eq Disabled' }, + ], reportName: `${tenant?.defaultDomainName}-transport-rules-list`, path: '/api/ListTransportRules', params: { TenantFilter: tenant?.defaultDomainName }, diff --git a/src/views/endpoint/intune/MEMAddPolicyTemplate.jsx b/src/views/endpoint/intune/MEMAddPolicyTemplate.jsx index 7d8e752d6a72..a6774de654dd 100644 --- a/src/views/endpoint/intune/MEMAddPolicyTemplate.jsx +++ b/src/views/endpoint/intune/MEMAddPolicyTemplate.jsx @@ -58,8 +58,10 @@ const MEMAddPolicyTemplate = () => { label="Select Policy Type" placeholder="Select a template type" values={[ + { label: 'App Protection Policy', value: 'AppProtection' }, { label: 'Administrative Template', value: 'Admin' }, { label: 'Settings Catalog', value: 'Catalog' }, + { label: 'Device Compliance Policy', value: 'deviceCompliancePolicies' }, { label: 'Custom Configuration', value: 'Device' }, ]} validate={required} diff --git a/src/views/home/Home.jsx b/src/views/home/Home.jsx index d5dfafb0e552..432bd10e0870 100644 --- a/src/views/home/Home.jsx +++ b/src/views/home/Home.jsx @@ -2,10 +2,8 @@ import React, { useState } from 'react' import { faBook, faCog, - faEllipsisH, faLaptopCode, faMailBulk, - faSearch, faUser, faUserFriends, faUserPlus, @@ -16,19 +14,15 @@ import { CCol, CCollapse, CDropdown, - CDropdownHeader, - CDropdownItem, CDropdownMenu, CDropdownToggle, CLink, - CNav, CRow, } from '@coreui/react' import { useGenericGetRequestQuery } from 'src/store/api/app' import { CippContentCard } from 'src/components/layout' import Skeleton from 'react-loading-skeleton' import { UniversalSearch } from 'src/components/utilities/UniversalSearch' -import { ActionContentCard } from 'src/components/contentcards' import { useSelector } from 'react-redux' import allStandardsList from 'src/data/standards' import Portals from 'src/data/portals' @@ -37,11 +31,14 @@ import { CChart } from '@coreui/react-chartjs' import { getStyle } from '@coreui/utils' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { Link } from 'react-router-dom' -import { useMediaPredicate } from 'react-media-hook' +import { useNavigate } from 'react-router-dom' +import { cellGenericFormatter } from 'src/components/tables/CellGenericFormat' +import { ModalService } from 'src/components/utilities' const TenantDashboard = () => { const [visible, setVisible] = useState(false) const [domainVisible, setDomainVisible] = useState(false) + const navigate = useNavigate() const currentTenant = useSelector((state) => state.app.currentTenant) const theme = useSelector((state) => state.app.currentTheme) @@ -174,6 +171,55 @@ const TenantDashboard = () => { ) }) } + + const handleTable = (data, title) => { + const QueryColumns = [] + const columns = Object.keys(data[0]).map((key) => { + QueryColumns.push({ + name: key, + selector: (row) => row[key], // Accessing the property using the key + sortable: true, + exportSelector: key, + cell: cellGenericFormatter(), + }) + }) + ModalService.open({ + data: data, + componentType: 'table', + componentProps: { + columns: QueryColumns, + keyField: 'id', + }, + title: title, + size: 'lg', + }) + } + + const userChartLegendClickHandler = function (e, legendItem, legend) { + switch (legendItem.text) { + case 'Total Users': + navigate('/identity/administration/users?customerId=' + currentTenant.customerId) + break + case 'Licensed Users': + navigate( + '/identity/administration/users?customerId=' + + currentTenant.customerId + + '&tableFilter=Graph%3A+assignedLicenses%2F%24count+ne+0', + ) + break + case 'Guests': + navigate( + '/identity/administration/users?customerId=' + + currentTenant.customerId + + '&tableFilter=Graph%3A+usertype+eq+%27guest%27', + ) + break + case 'Global Admins': + handleTable(GlobalAdminList.data?.Results, 'Global Admins') + break + } + } + return ( <> @@ -333,10 +379,17 @@ const TenantDashboard = () => { options={{ plugins: { legend: { - position: 'bottom', + position: 'left', labels: { color: getStyle('--cui-body-color'), }, + onClick: userChartLegendClickHandler, + onHover: (event) => { + event.native.target.style.cursor = 'pointer' + }, + onLeave: (event) => { + event.native.target.style.cursor = 'default' + }, }, }, }} @@ -422,14 +475,20 @@ const TenantDashboard = () => { { link for more information. )} + {user?.onPremisesSyncEnabled === true && ( + + Warning! This user Active Directory sync enabled. Edits should be made from a Domain + Controller. + + )} {postResults.isSuccess && ( {postResults.data?.Results} )} @@ -180,7 +186,7 @@ const EditUser = () => {
    )} - + {userIsFetching && } diff --git a/src/views/identity/administration/OffboardingWizard.jsx b/src/views/identity/administration/OffboardingWizard.jsx index e7ba79912958..b5b344969c7a 100644 --- a/src/views/identity/administration/OffboardingWizard.jsx +++ b/src/views/identity/administration/OffboardingWizard.jsx @@ -1,5 +1,5 @@ import React, { useState } from 'react' -import { CCallout, CCol, CListGroup, CListGroupItem, CRow, CSpinner } from '@coreui/react' +import { CCallout, CCol, CListGroup, CListGroupItem, CRow, CSpinner, CTooltip } from '@coreui/react' import { Field, FormSpy } from 'react-final-form' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { faExclamationTriangle, faTimes, faCheck } from '@fortawesome/free-solid-svg-icons' @@ -296,7 +296,21 @@ const OffboardingWizard = () => { className="d-flex justify-content-between align-items-center" >
    Selected User:
    - {user.value} + + {users.find((x) => x.userPrincipalName === user.value) + .onPremisesSyncEnabled === true ? ( + + + + ) : ( + '' + )} + {user.value} + ))} diff --git a/src/views/identity/administration/Users.jsx b/src/views/identity/administration/Users.jsx index dc3b5243815b..2d0a4aa9ac49 100644 --- a/src/views/identity/administration/Users.jsx +++ b/src/views/identity/administration/Users.jsx @@ -480,6 +480,7 @@ const Users = (row) => { 'id,accountEnabled,businessPhones,city,createdDateTime,companyName,country,department,displayName,faxNumber,givenName,isResourceAccount,jobTitle,mail,mailNickname,mobilePhone,onPremisesDistinguishedName,officeLocation,onPremisesLastSyncDateTime,otherMails,postalCode,preferredDataLocation,preferredLanguage,proxyAddresses,showInAddressList,state,streetAddress,surname,usageLocation,userPrincipalName,userType,assignedLicenses,onPremisesSyncEnabled', $count: true, $orderby: 'displayName', + $top: 999, }, tableProps: { keyField: 'id', diff --git a/src/views/identity/reports/MFAReport.jsx b/src/views/identity/reports/MFAReport.jsx index 70ece62c2b4e..4f1d80936fa2 100644 --- a/src/views/identity/reports/MFAReport.jsx +++ b/src/views/identity/reports/MFAReport.jsx @@ -139,11 +139,15 @@ const MFAList = () => { { filterName: 'Enabled, licensed non-guest users missing MFA', filter: - 'Complex: UPN notlike #EXT#; IsLicensed eq true; accountEnabled eq true; MFARegistration eq false', + 'Complex: UPN notlike #EXT#; IsLicensed eq true; accountEnabled eq true; MFARegistration ne true', }, { filterName: 'No MFA methods registered', - filter: 'Complex: MFARegistration eq false', + filter: 'Complex: MFARegistration ne true', + }, + { + filterName: 'MFA methods registered', + filter: 'Complex: MFARegistration eq true', }, ], columns: tenant.defaultDomainName === 'AllTenants' ? Altcolumns : columns, diff --git a/src/views/tenant/administration/AlertWizard.jsx b/src/views/tenant/administration/AlertWizard.jsx index c2e920354e15..2c3bf20bf0cf 100644 --- a/src/views/tenant/administration/AlertWizard.jsx +++ b/src/views/tenant/administration/AlertWizard.jsx @@ -1,346 +1,444 @@ -import React from 'react' -import { CCol, CRow, CForm, CListGroup, CListGroupItem, CCallout, CSpinner } from '@coreui/react' -import { Field, FormSpy } from 'react-final-form' -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' -import { faCheck, faExclamationTriangle, faTimes } from '@fortawesome/free-solid-svg-icons' -import { CippWizard } from 'src/components/layout' -import { WizardTableField } from 'src/components/tables' -import PropTypes from 'prop-types' -import { Condition, RFFCFormSelect, RFFCFormSwitch, RFFSelectSearch } from 'src/components/forms' +import React, { useState } from 'react' +import { CBadge, CButton, CCallout, CCol, CForm, CRow, CSpinner } from '@coreui/react' +import useQuery from 'src/hooks/useQuery' +import { useDispatch, useSelector } from 'react-redux' +import { Field, Form, FormSpy } from 'react-final-form' +import { CippPage } from 'src/components/layout' +import { TenantSelector, TenantSelectorMultiple } from 'src/components/utilities' +import { + Condition, + RFFCFormInput, + RFFCFormSwitch, + RFFSelectSearch, +} from 'src/components/forms/RFFComponents' +import { useListTenantQuery } from 'src/store/api/tenants' import { useLazyGenericPostRequestQuery } from 'src/store/api/app' -import countryList from 'src/data/countryList.json' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { faCircleNotch } from '@fortawesome/free-solid-svg-icons' +import CippButtonCard from 'src/components/contentcards/CippButtonCard' +import alertList from 'src/data/alerts.json' +import auditLogSchema from 'src/data/AuditLogSchema.json' +import auditLogTemplates from 'src/data/AuditLogTemplates.json' +import Skeleton from 'react-loading-skeleton' +import { required } from 'src/validators' + +const AlertWizard = () => { + const tenantDomain = useSelector((state) => state.app.currentTenant.defaultDomainName) + const [genericPostRequest, postResults] = useLazyGenericPostRequestQuery() + const [alertType, setAlertType] = useState(false) + const [recommendedRecurrence, setRecommendedRecurrence] = useState() + const [currentFormState, setCurrentFormState] = useState() + const [selectedTenant, setSelectedTenant] = useState([]) + const { data: tenant = {}, isFetching, error, isSuccess } = useListTenantQuery(tenantDomain) + + const onSubmitAudit = (values) => { + genericPostRequest({ path: '/api/addAlert', values }).then((res) => {}) + } -const Error = ({ name }) => ( - - touched && error ? ( - - - {error} - - ) : null + const onSubmitScript = (values) => { + //get current time as startDate, to the closest 15 minutes in the future + const startDate = new Date() + startDate.setMinutes(startDate.getMinutes() + 15 - (startDate.getMinutes() % 15)) + //unix time, minus a couple of seconds to ensure it runs after the current time + const unixTime = Math.floor(startDate.getTime() / 1000) - 45 + const shippedValues = { + TenantFilter: tenantDomain, + Name: `${values.command.label} for ${tenantDomain}`, + Command: { value: `Get-CIPPAlert${values.command.value.name}` }, + Parameters: { input: values.input }, + ScheduledTime: unixTime, + Recurrence: values.Recurrence, + PostExecution: { + Webhook: values.webhook, + Email: values.email, + PSA: values.psa, + }, } - /> -) + genericPostRequest({ path: '/api/AddScheduledItem?hidden=true', values: shippedValues }).then( + (res) => {}, + ) + } -Error.propTypes = { - name: PropTypes.string.isRequired, -} + const initialValues = { + ...tenant[0], + ...currentFormState?.values, + ...recommendedRecurrence, + } + const [auditFormState, setAuditFormState] = useState() -const requiredArray = (value) => (value && value.length !== 0 ? undefined : 'Required') + const initialValuesAudit = { + tenantFilter: [...selectedTenant], + ...auditFormState, + } + const recurrenceOptions = [ + { value: '30m', name: 'Every 30 minutes' }, + { value: '1h', name: 'Every hour' }, + { value: '4h', name: 'Every 4 hours' }, + { value: '1d', name: 'Every 1 day' }, + { value: '7d', name: 'Every 7 days' }, + { value: '30d', name: 'Every 30 days' }, + { value: '365d', name: 'Every 365 days' }, + ] -const AlertWizard = () => { - const [genericPostRequest, postResults] = useLazyGenericPostRequestQuery() + const presetValues = auditLogTemplates - const handleSubmit = async (values) => { - Object.keys(values).filter(function (x) { - if (values[x] === null) { - delete values[x] - } - return null - }) - values.selectedTenants.map( - (tenant) => (values[`Select_${tenant.defaultDomainName}`] = tenant.defaultDomainName), - ) - genericPostRequest({ path: '/api/AddAlert', values: values }) + const getAuditLogSchema = (logbook) => { + const common = auditLogSchema.Common + const log = auditLogSchema[logbook] + const combined = { ...common, ...log } + return Object.keys(combined).map((key) => ({ + name: key, + value: combined[key], + })) } + const [addedEvent, setAddedEvent] = React.useState(1) - const formValues = {} + const getRecurrenceOptions = () => { + const values = currentFormState?.values + if (values) { + //console.log(currentFormState) + const updatedRecurrenceOptions = recurrenceOptions.map((opt) => ({ + ...opt, + name: opt.name.replace(' (Recommended)', ''), + })) + const recommendedValue = values.command?.value?.recommendedRunInterval + const option = updatedRecurrenceOptions.find((opt) => opt.value === recommendedValue) + if (option) { + option.name += ' (Recommended)' + if (option.value !== recommendedRecurrence?.Recurrence.value) { + setRecommendedRecurrence({ Recurrence: { value: option.value, label: option.name } }) + } + } + return updatedRecurrenceOptions + } + } - return ( - - -
    -

    Step 1

    -
    Choose a tenant
    -
    -
    - - {(props) => ( - row['displayName'], - sortable: true, - exportselector: 'displayName', - }, - { - name: 'Default Domain Name', - selector: (row) => row['defaultDomainName'], - sortable: true, - exportselector: 'mail', - }, - ]} - fieldProps={props} - /> - )} - - -
    -
    - -
    -

    Step 2

    -
    Select Legacy Alerts to receive
    -
    -
    - -

    - Alerts setup on this page are considered legacy. These alerts run every 15 minutes and - do not use our advanced alerting engine. -

    - - - -
    - - - - - - - + const setAuditForm = (e) => { + const preset = presetValues.find((p) => p.value === e.value) + setAuditFormState(preset.template) + setAddedEvent(preset.template.conditions.length) + } - - - - - - - - - - - - - -
    -
    -
    + const dovalues = [ + //{ value: 'cippcommand', label: 'Execute a CIPP Command' }, + { value: 'becremediate', name: 'Execute a BEC Remediate' }, + { value: 'disableuser', name: 'Disable the user in the log entry' }, + // { value: 'generatelog', label: 'Generate a log entry' }, + { value: 'generatemail', name: 'Generate an email' }, + { value: 'generatePSA', name: 'Generate a PSA ticket' }, + { value: 'generateWebhook', name: 'Generate a webhook' }, + ] -
    -
    - -
    -

    Step 3

    -
    Select webhook alerts
    -
    -
    - -

    - This setting will subscribe CIPP to receive the audit logs from this tenant directly. - You can then use the Alert Rules page to create alerts or take actions based on these - logs. -

    - - - + return ( + + {isFetching && } + {!isFetching && ( + <> + + + setAlertType('audit')}>Select} + > + Select this option if you'd like to create an alert based on a received Microsoft + Audit log. + + + + setAlertType('script')}>Select} + > + Select this option if you'd like to setup an alert based on data processed by CIPP + -
    -
    -
    - -
    -

    Step 3

    -
    Confirm and apply
    -
    -
    - {!postResults.isSuccess && ( - - {/* eslint-disable react/prop-types */} - {(props) => { - return ( - <> - - - - - - Alert on users without any form of MFA - - - - Alert on admins without any form of MFA - - - - Alert on new users added to any admin role - - - - Alert on changed admin Passwords - - - - Alert if Defender is not running - - - - Alert on Defender Malware - - - - Alert on 90% mailbox quota used - - - - Alert on unused licenses - - - - Alert on overused licenses - - - - Alert on expiring application secrets - - - - Alert on expiring APN certificates - - - - Alert on expiring VPP tokens - - - - Alert on expiring DEP tokens - - - - Alert on no CA policies - - - - Alert on Security Defaults automatic enablement - - - - - - - ) - }} - - )} - {postResults.isFetching && ( - - Loading - - )} - {postResults.isSuccess && ( - - {postResults.data.Results.map((message, idx) => { - return
  • {message}
  • - })} -
    - )} -
    -
    -
    + {alertType === 'audit' && ( + <> + + + + Select the tenants you want to include in this Alert. + setSelectedTenant(e)} + AllTenants={true} + valueisDomain={true} + /> + + + + { + return ( + + + + + {postResults.isFetching && } Save Alert + + } + > + + + setAuditForm(e)} + /> + + + + + + + + {addedEvent > 0 && + [...Array(addedEvent)].map((e, i) => ( + + + + AND + + + + + {(props) => { + return ( + + ) + }} + + + + + + + + {(props) => { + return ( + <> + {props.values?.conditions?.[i]?.Property?.value === + 'String' && ( + + )} + {props.values?.conditions?.[ + i + ]?.Property?.value.startsWith('List:') && ( + + )} + + ) + }} + + + + ))} + + + {addedEvent > 0 && ( + setAddedEvent(addedEvent - 1)} + className={`circular-button`} + title={'-'} + > + + + )} + {addedEvent < 4 && ( + setAddedEvent(addedEvent + 1)} + className={`circular-button`} + title={'+'} + > + + + )} + + + + + + + {postResults.isSuccess && ( + +
  • {postResults.data.Results}
  • +
    + )} +
    +
    +
    +
    +
    + ) + }} + /> + + )} + {alertType === 'script' && ( + <> + + + +

    Select the tenants you want to include in this Alert.

    + +
    +
    +
    + + + + Save Alert + {postResults.isFetching && ( + + )} + + } + > + { + return ( + + + + ({ + value: cmd, + name: cmd.label, + }))} + name="command" + placeholder={'Select a command'} + label="What alerting script should run" + validate={required} + /> + + + + + + + {(props) => { + return ( + + ) + }} + + + + + + + setCurrentFormState(formvalues)} + /> + + + + + + + + + + + + {postResults.isSuccess && ( + +
  • {postResults.data.Results}
  • +
    + )} +
    + ) + }} + /> +
    +
    +
    + + )} + + )} + ) } diff --git a/src/views/tenant/administration/GraphExplorer.jsx b/src/views/tenant/administration/GraphExplorer.jsx index bad378c27d3a..3d6950c598d4 100644 --- a/src/views/tenant/administration/GraphExplorer.jsx +++ b/src/views/tenant/administration/GraphExplorer.jsx @@ -75,7 +75,38 @@ const GraphExplorer = () => { }, []) if (graphrequest.isSuccess) { - if (graphrequest.data?.Results?.length > 0) { + if ( + graphrequest.data?.Metadata?.Parameters?.$select !== undefined && + graphrequest.data?.Metadata?.Parameters?.$select !== '' && + graphrequest.data?.Metadata?.Parameters?.$select !== null + ) { + //set columns + if (graphrequest.data?.Metadata?.TenantFilter === 'AllTenants') { + QueryColumns.data.push({ + name: 'Tenant', + selector: (row) => row['Tenant'], + sortable: true, + exportSelector: 'Tenant', + cell: cellGenericFormatter(), + }) + QueryColumns.data.push({ + name: 'CippStatus', + selector: (row) => row['CippStatus'], + sortable: true, + exportSelector: 'CippStatus', + cell: cellGenericFormatter(), + }) + } + graphrequest.data?.Metadata?.Parameters?.$select.split(',')?.map((value) => + QueryColumns.data.push({ + name: value, + selector: (row) => row[`${value.toString()}`], + sortable: true, + exportSelector: value, + cell: cellGenericFormatter(), + }), + ) + } else if (graphrequest.data?.Results?.length > 0) { //set columns Object.keys(graphrequest.data?.Results[0]).map((value) => QueryColumns.data.push({ @@ -121,7 +152,7 @@ const GraphExplorer = () => { { name: 'All users with email addresses', id: '6164e239-0c9a-4a27-9049-6250bf65a3e3', - params: { endpoint: '/users', $select: 'userprincipalname,mail,proxyAddresses', $filter: '' }, + params: { endpoint: '/users', $select: 'userPrincipalName,mail,proxyAddresses', $filter: '' }, isBuiltin: true, }, { @@ -248,6 +279,9 @@ const GraphExplorer = () => { if (params?.$select) { select = params.$select.map((p) => p.value).join(',') } + if (params?.name) { + params.QueueNameOverride = 'Graph Explorer - ' + params.name + } execGraphRequest({ path: 'api/ListGraphRequest', params: { @@ -505,6 +539,13 @@ const GraphExplorer = () => { label="Reverse Tenant Lookup" /> + +
    { const [ExecuteGetRequest, getResults] = useLazyGenericGetRequestQuery() - - const Offcanvas = (row, rowIndex, formatExtraData) => { - const [ocVisible, setOCVisible] = useState(false) - - const handleDeleteSchedule = (apiurl, message) => { - ModalService.confirm({ - title: 'Confirm', - body:
    {message}
    , - onConfirm: () => - ExecuteGetRequest({ path: apiurl }).then((res) => { - setRefreshState(res.requestId) - }), - confirmLabel: 'Continue', - cancelLabel: 'Cancel', - }) - } - let jsonResults - try { - jsonResults = JSON.parse(row) - } catch (error) { - jsonResults = row - } - - return ( - <> - - setOCVisible(true)}> - - - - - - handleDeleteSchedule( - `/api/RemoveQueuedAlert?&ID=${row.tenantId}`, - 'Do you want to delete the queued alert?', - ) - } - size="sm" - variant="ghost" - color="danger" - > - - - - ({ - label: key, - value: - typeof row[key] === 'boolean' ? ( - row[key] ? ( - - ) : ( - - ) - ) : ( - row[key] - ), - }))} - placement="end" - visible={ocVisible} - id={row.id} - hideFunction={() => setOCVisible(false)} - /> - - ) - } - const tenantDomain = useSelector((state) => state.app.currentTenant.defaultDomainName) const [refreshState, setRefreshState] = useState(false) const [genericPostRequest, postResults] = useLazyGenericPostRequestQuery() - const onSubmit = (values) => { - Object.keys(values).filter(function (x) { - if (values[x] === null || values[x] === 0) { - delete values[x] - } - return null - }) - values['tenantFilter'] = tenantDomain - values['SetAlerts'] = true - genericPostRequest({ path: '/api/AddAlert', values: values }).then((res) => { - setRefreshState(res.requestId) - }) - } - const { data: currentlySelectedAlerts = [], isLoading: isLoadingCurrentAlerts } = - useGenericGetRequestQuery({ - path: `api/ListAlertsQueue?TenantFilter=${tenantDomain}&RefreshGuid=${refreshState}`, - }) const columns = [ { - name: 'Tenant Name', - selector: (row) => row['tenantName'], + name: 'Tenant(s)', + selector: (row) => row['Tenants'], sortable: true, - cell: (row) => CellTip(row['tenantName']), - exportSelector: 'tenantName', + cell: (row) => CellTip(row['Tenants']), + exportSelector: 'Tenants', }, { - name: 'Tenant ID', - selector: (row) => row['tenantId'], + name: 'Events', + selector: (row) => row['Conditions'], sortable: true, - cell: (row) => CellTip(row['tenantId']), - exportSelector: 'tenantId', + cell: (row) => CellTip(row['Conditions']), + exportSelector: 'Conditions', }, { - name: 'Actions', - cell: Offcanvas, - maxWidth: '80px', + name: 'Actions to take', + selector: (row) => row['Actions'], + sortable: true, + cell: cellBadgeFormatter({ color: 'info' }), + exportSelector: 'Actions', + }, + { + name: 'Repeats every', + selector: (row) => row['RepeatsEvery'], + sortable: true, + cell: (row) => CellTip(row['RepeatsEvery']), + exportSelector: 'RepeatsEvery', + }, + { + name: 'Event Type', + selector: (row) => row['EventType'], + sortable: true, + cell: cellBadgeFormatter({ color: 'info' }), + exportSelector: 'EventType', }, ] - const initialValues = currentlySelectedAlerts.filter((x) => x.tenantName === tenantDomain)[0] - const allTenantsAlert = currentlySelectedAlerts.find( - (tenant) => tenant.tenantName === 'AllTenants', - ) - function getLabel(item) { - if (typeof allTenantsAlert === 'object' && allTenantsAlert !== null) { - if (allTenantsAlert[`${item}`]) { - return `* Enabled via All Tenants` - } - } - return '' - } return ( <> - - - { - return ( - -

    - Classic Alerts are sent every 15 minutes, with a maximum of 1 unique alert - per 24 hours. These alerts do not use the Alert Rules Engine. -

    - {isLoadingCurrentAlerts && } - - - - {(props) => } - - - -
    - {alertsList.map((alert, index) => ( - - - {alert.requiresInput && ( - - {({ values }) => { - if (values[alert.name]) { - return ( - - ) - } - return null - }} - - )} - - ))} -
    - - - - Set Alerts - {postResults.isFetching && ( - - )} - - - - {postResults.isSuccess && ( - -
  • {postResults.data.Results}
  • -
    - )} - {getResults.isFetching && ( - - Loading - - )} - {getResults.isSuccess && ( - {getResults.data?.Results} - )} - {getResults.isError && ( - - Could not connect to API: {getResults.error.message} - - )} -
    - ) - }} - /> -
    -
    - + { helpContext: 'https://google.com', }} title="Alerts List" + titleButton={ + + } tenantSelector={false} datatable={{ tableProps: { selectableRows: true, actionsList: [ { - label: 'Delete task', + label: 'Delete Alert', modal: true, - modalUrl: `/api/RemoveQueuedAlert?&ID=!tenantId`, + modalUrl: `/api/RemoveQueuedAlert?&ID=!RowKey&EventType=!EventType&RefreshGuid=${refreshState}`, modalMessage: 'Do you want to delete this job?', }, ], }, columns, - reportName: `Scheduled-Jobs`, + reportName: `Alerts`, path: `/api/ListAlertsQueue?RefreshGuid=${refreshState}`, }} /> diff --git a/src/views/tenant/administration/ListEnterpriseApps.jsx b/src/views/tenant/administration/ListEnterpriseApps.jsx index 5b2385a6c0f1..bf49da8c0b67 100644 --- a/src/views/tenant/administration/ListEnterpriseApps.jsx +++ b/src/views/tenant/administration/ListEnterpriseApps.jsx @@ -90,6 +90,11 @@ const EnterpriseApplications = () => { filter: "Graph: tags/any(t:t eq 'WindowsAzureActiveDirectoryGalleryApplicationPrimaryV1')", }, + { + filterName: 'All non-Microsoft Enterprise Apps', + filter: + 'Complex: appOwnerOrganizationId notlike f8cdef31-a31e-4b4a-93e4-5f571e91255a', + }, ], tableProps: { selectableRows: true, diff --git a/src/views/tenant/administration/ListGDAPRelationships.jsx b/src/views/tenant/administration/ListGDAPRelationships.jsx index c463797c16c9..11ae56e89e33 100644 --- a/src/views/tenant/administration/ListGDAPRelationships.jsx +++ b/src/views/tenant/administration/ListGDAPRelationships.jsx @@ -111,6 +111,14 @@ const Actions = (row, rowIndex, formatExtraData) => { modalUrl: `/api/ExecAutoExtendGDAP?ID=${row.id}`, modalMessage: 'Are you sure you want to enable auto-extend for this relationship', }, + { + label: 'Remove Global Administrator from Relationship', + color: 'danger', + modal: true, + modalUrl: `/api/ExecGDAPRemoveGArole?&GDAPID=${row.id}`, + modalMessage: + 'Are you sure you want to remove Global Administrator from this relationship?', + }, { label: 'Terminate Relationship', color: 'danger', @@ -218,6 +226,13 @@ const GDAPRelationships = () => { tableProps: { selectableRows: true, actionsList: [ + { + label: 'Remove Global Administrator from Relationship', + modal: true, + modalUrl: `/api/ExecGDAPRemoveGArole?&GDAPID=!id`, + modalMessage: + 'Are you sure you want to remove Global Administrator from these relationship(s)?', + }, { label: 'Terminate Relationship', modal: true, diff --git a/src/views/tenant/administration/SecureScore.jsx b/src/views/tenant/administration/SecureScore.jsx index 2af727c7b9dc..76c77d584296 100644 --- a/src/views/tenant/administration/SecureScore.jsx +++ b/src/views/tenant/administration/SecureScore.jsx @@ -26,6 +26,7 @@ import { useNavigate } from 'react-router-dom' 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' const SecureScore = () => { const textRef = useRef() @@ -220,93 +221,60 @@ const SecureScore = () => { - - - Current Score - - - - {isFetching && } - {translateState && ( - <> -

    - {Math.round( - (translateData?.currentScore / translateData?.maxScore) * 100 * 10, - ) / 10} - % -

    - - {translateData?.currentScore} of {translateData?.maxScore} points - - - )} -
    -
    -
    +
    - - - Compared Score (Similiar sized business) - - - - {isFetching && } - {translateState && ( - <> -

    - { - //calculate percentage, round to 1 dec. - Math.round( - (translateData?.averageComparativeScores[1]?.averageScore / - translateData?.maxScore) * - 100 * - 10, - ) / 10 - } - % -

    - - {translateData?.averageComparativeScores[1]?.averageScore} of{' '} - {translateData?.maxScore} points - - - )} -
    -
    -
    +
    - - - Compared Score (All businesses) - - - - {isFetching && } - {translateState && ( - <> -

    - { - //calculate percentage, round to 1 dec. - Math.round( - (translateData?.averageComparativeScores[0]?.averageScore / - translateData?.maxScore) * - 100 * - 10, - ) / 10 - } - % -

    - - {translateData?.averageComparativeScores[0]?.averageScore} of{' '} - {translateData?.maxScore} points - - - )} -
    -
    -
    +
    diff --git a/src/views/tenant/administration/TenantLookup.jsx b/src/views/tenant/administration/TenantLookup.jsx index abb95f701571..ced62911ae8d 100644 --- a/src/views/tenant/administration/TenantLookup.jsx +++ b/src/views/tenant/administration/TenantLookup.jsx @@ -122,25 +122,27 @@ const GraphExplorer = () => {

    Tenant Name

    {graphrequest.isFetching && } - {graphrequest.data?.GraphRequest.displayName} + {graphrequest.data?.GraphRequest?.displayName + ? graphrequest.data?.GraphRequest?.displayName + : 'Could not find tenant - Is this a M365 domain name?'}

    Tenant ID

    {graphrequest.isFetching && } - {graphrequest.data?.GraphRequest.tenantId} + {graphrequest.data?.GraphRequest?.tenantId}

    Default Domain Name

    {graphrequest.isFetching && } - {graphrequest.data?.GraphRequest.defaultDomainName} + {graphrequest.data?.GraphRequest?.defaultDomainName}

    Tenant Brand Name

    {graphrequest.isFetching && } - {graphrequest.data?.GraphRequest.federationBrandName} - {graphrequest.data?.GraphRequest.federationBrandName === null && + {graphrequest.data?.GraphRequest?.federationBrandName} + {graphrequest.data?.GraphRequest?.federationBrandName === null && 'No brand name set'}
    diff --git a/src/views/tenant/administration/TenantOnboarding.jsx b/src/views/tenant/administration/TenantOnboarding.jsx new file mode 100644 index 000000000000..94afd7437938 --- /dev/null +++ b/src/views/tenant/administration/TenantOnboarding.jsx @@ -0,0 +1,153 @@ +import { CBadge, CTooltip } from '@coreui/react' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import React from 'react' +import { TitleButton } from 'src/components/buttons' +import { CippPageList } from 'src/components/layout' +import { CellBadge, cellDateFormatter } from 'src/components/tables' +import { cellGenericFormatter } from 'src/components/tables/CellGenericFormat' + +const TenantOnboarding = () => { + const titleButton = ( + + ) + function ucfirst(str) { + return str.charAt(0).toUpperCase() + str.slice(1) + } + function getBadgeColor(status) { + switch (status.toLowerCase()) { + case 'queued': + return 'info' + case 'failed': + return 'danger' + case 'succeeded': + return 'success' + case 'running': + return 'primary' + } + } + function getLatestStep(steps) { + var activeSteps = steps?.filter((step) => step.Status !== 'pending') + var currentStep = activeSteps[activeSteps.length - 1] + var color = 'info' + var icon = 'me-2 info-circle' + var spin = false + switch (currentStep?.Status) { + case 'succeeded': + color = 'me-2 text-success' + icon = 'check-circle' + break + case 'failed': + color = 'me-2 text-danger' + icon = 'times-circle' + break + case 'running': + color = 'me-2 text-primary' + icon = 'sync' + spin = true + break + } + return ( + +
    + + {currentStep?.Title} +
    +
    + ) + } + const columns = [ + { + name: 'Last Update', + selector: (row) => row.Timestamp, + sortable: true, + exportSelector: 'Timestamp', + cell: cellDateFormatter({ format: 'short' }), + }, + { + name: 'Tenant', + selector: (row) => row?.Relationship?.customer?.displayName, + sortable: true, + cell: cellGenericFormatter(), + exportSelector: 'Relationship/customer/displayName', + }, + { + name: 'Status', + selector: (row) => row?.Status, + sortable: true, + exportSelector: 'Status', + cell: (row) => CellBadge({ label: ucfirst(row?.Status), color: getBadgeColor(row?.Status) }), + }, + { + name: 'Onboarding Step', + selector: (row) => row?.OnboardingSteps, + cell: (row) => getLatestStep(row?.OnboardingSteps), + }, + { + name: 'Logs', + selector: (row) => row?.Logs, + sortable: false, + cell: cellGenericFormatter(), + }, + ] + return ( +
    + +
    + ) +} + +export default TenantOnboarding diff --git a/src/views/tenant/administration/TenantOnboardingWizard.jsx b/src/views/tenant/administration/TenantOnboardingWizard.jsx index 122765dff6c3..ebbab2a8e178 100644 --- a/src/views/tenant/administration/TenantOnboardingWizard.jsx +++ b/src/views/tenant/administration/TenantOnboardingWizard.jsx @@ -1,32 +1,16 @@ -import React, { useState, useRef, useEffect } from 'react' -import { - CAccordion, - CAccordionBody, - CAccordionHeader, - CAccordionItem, - CButton, - CCallout, - CCol, - CRow, - CSpinner, -} from '@coreui/react' +import React, { useRef, useEffect } from 'react' +import { CAccordion, CCallout, CCol, CRow } from '@coreui/react' import { Field, FormSpy } from 'react-final-form' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { faExclamationTriangle, faTimes, faCheck } from '@fortawesome/free-solid-svg-icons' import { useSelector } from 'react-redux' import { CippWizard } from 'src/components/layout' import PropTypes from 'prop-types' -import { RFFCFormCheck, RFFCFormInput, RFFCFormSwitch, RFFSelectSearch } from 'src/components/forms' -import { CippCodeBlock, TenantSelector } from 'src/components/utilities' +import { RFFCFormSwitch } from 'src/components/forms' import { useLazyGenericPostRequestQuery } from 'src/store/api/app' -import { - CellDate, - WizardTableField, - cellDateFormatter, - cellNullTextFormatter, -} from 'src/components/tables' -import ReactTimeAgo from 'react-time-ago' -import { TableModalButton, TitleButton } from 'src/components/buttons' +import { WizardTableField, cellDateFormatter, cellNullTextFormatter } from 'src/components/tables' +import { TitleButton } from 'src/components/buttons' +import RelationshipOnboarding from 'src/views/tenant/administration/onboarding/RelationshipOnboarding' const Error = ({ name }) => ( { - const [relationshipReady, setRelationshipReady] = useState(false) - const [refreshGuid, setRefreshGuid] = useState(false) - const [getOnboardingStatus, onboardingStatus] = useLazyGenericPostRequestQuery() - var headerIcon = relationshipReady ? 'check-circle' : 'question-circle' - - useInterval( - async () => { - if (onboardingStatus.data?.Status == 'running' || onboardingStatus.data?.Status == 'queued') { - getOnboardingStatus({ - path: '/api/ExecOnboardTenant', - values: { id: relationship.id }, - }) - } - }, - 5000, - onboardingStatus.data, - ) - - return ( - - - {onboardingStatus?.data?.Status == 'running' ? ( - - ) : ( - - )} - Onboarding Relationship: {} - {relationship.displayName} - - - - {(relationship?.customer?.displayName || - onboardingStatus?.data?.Relationship?.customer?.displayName) && ( - -

    Customer

    - {onboardingStatus?.data?.Relationship?.customer?.displayName - ? onboardingStatus?.data?.Relationship?.customer?.displayName - : relationship.customer.displayName} -
    - )} - {onboardingStatus?.data?.Timestamp && ( - -

    Last Updated

    - -
    - )} - -

    Relationship Status

    - {relationship.status} -
    - -

    Creation Date

    - -
    - {relationship.status == 'approvalPending' && - onboardingStatus?.data?.Relationship?.status != 'active' && ( - -

    Invite URL

    - -
    - )} -
    - {onboardingStatus.isUninitialized && - getOnboardingStatus({ - path: '/api/ExecOnboardTenant', - values: { id: relationship.id, gdapRoles, autoMapRoles, addMissingGroups }, - })} - {onboardingStatus.isSuccess && ( - <> - {onboardingStatus.data?.Status != 'queued' && ( - - getOnboardingStatus({ - path: '/api/ExecOnboardTenant?Retry=True', - values: { id: relationship.id, gdapRoles, autoMapRoles, addMissingGroups }, - }) - } - className="mb-3 me-2" - > - Retry - - )} - {onboardingStatus.data?.Logs && ( - - )} -
    - {onboardingStatus.data?.OnboardingSteps?.map((step, idx) => ( - - - {step.Status == 'running' ? ( - - ) : ( - - )}{' '} - {step.Title} - - - {step.Message} - - - ))} - - )} -
    -
    - ) -} -RelationshipOnboarding.propTypes = { - relationship: PropTypes.object.isRequired, - gdapRoles: PropTypes.array, - autoMapRoles: PropTypes.bool, - addMissingGroups: PropTypes.bool, -} - const TenantOnboardingWizard = () => { const tenantDomain = useSelector((state) => state.app.currentTenant.defaultDomainName) const currentSettings = useSelector((state) => state.app) @@ -350,6 +183,20 @@ const TenantOnboardingWizard = () => {
    Tenant Onboarding Options

    +
    Standards
    + +
    Optional Settings
    +

    + Use these options for relationships created outside of the CIPP Invite Wizard or if the + SAM user is missing required GDAP groups from the Partner Tenant. +

    @@ -419,6 +266,7 @@ const TenantOnboardingWizard = () => { gdapRoles={props.values.gdapRoles} autoMapRoles={props.values.autoMapRoles} addMissingGroups={props.values.addMissingGroups} + standardsExcludeAllTenants={props.values.standardsExcludeAllTenants} key={idx} /> ))} diff --git a/src/views/tenant/administration/onboarding/RelationshipOnboarding.jsx b/src/views/tenant/administration/onboarding/RelationshipOnboarding.jsx new file mode 100644 index 000000000000..9147caebbf63 --- /dev/null +++ b/src/views/tenant/administration/onboarding/RelationshipOnboarding.jsx @@ -0,0 +1,212 @@ +import React, { useState, useRef, useEffect } from 'react' +import { + CAccordionBody, + CAccordionHeader, + CAccordionItem, + CButton, + CCallout, + CCol, + CRow, + CSpinner, +} from '@coreui/react' +import { Field } from 'react-final-form' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { faExclamationTriangle, faTimes, faCheck } from '@fortawesome/free-solid-svg-icons' +import PropTypes from 'prop-types' +import { CippCodeBlock, TenantSelector } from 'src/components/utilities' +import { useLazyGenericPostRequestQuery } from 'src/store/api/app' +import { CellDate } from 'src/components/tables' +import ReactTimeAgo from 'react-time-ago' +import { TableModalButton, TitleButton } from 'src/components/buttons' + +function useInterval(callback, delay, state) { + const savedCallback = useRef() + + // Remember the latest callback. + useEffect(() => { + savedCallback.current = callback + }) + + // Set up the interval. + useEffect(() => { + function tick() { + savedCallback.current() + } + + if (delay !== null) { + let id = setInterval(tick, delay) + return () => clearInterval(id) + } + }, [delay, state]) +} + +const RelationshipOnboarding = ({ + relationship, + gdapRoles, + autoMapRoles, + addMissingGroups, + standardsExcludeAllTenants, +}) => { + const [getOnboardingStatus, onboardingStatus] = useLazyGenericPostRequestQuery() + + useInterval( + async () => { + if (onboardingStatus.data?.Status == 'running' || onboardingStatus.data?.Status == 'queued') { + getOnboardingStatus({ + path: `/api/ExecOnboardTenant`, + values: { id: relationship.id }, + }) + } + }, + 5000, + onboardingStatus.data, + ) + console.log(standardsExcludeAllTenants) + + return ( + + + {onboardingStatus?.data?.Status == 'running' ? ( + + ) : ( + + )} + Onboarding Relationship: {} + {relationship.displayName} + + + + {(relationship?.customer?.displayName || + onboardingStatus?.data?.Relationship?.customer?.displayName) && ( + +

    Customer

    + {onboardingStatus?.data?.Relationship?.customer?.displayName + ? onboardingStatus?.data?.Relationship?.customer?.displayName + : relationship.customer.displayName} +
    + )} + {onboardingStatus?.data?.Timestamp && ( + +

    Last Updated

    + +
    + )} + +

    Relationship Status

    + {relationship.status} +
    + +

    Creation Date

    + +
    + {relationship.status == 'approvalPending' && + onboardingStatus?.data?.Relationship?.status != 'active' && ( + +

    Invite URL

    + +
    + )} +
    + {onboardingStatus.isUninitialized && + getOnboardingStatus({ + path: '/api/ExecOnboardTenant', + values: { + id: relationship.id, + gdapRoles, + autoMapRoles, + addMissingGroups, + standardsExcludeAllTenants, + }, + })} + {onboardingStatus.isSuccess && ( + <> + {onboardingStatus.data?.Status != 'queued' && ( + + getOnboardingStatus({ + path: '/api/ExecOnboardTenant?Retry=True', + values: { + id: relationship.id, + gdapRoles, + autoMapRoles, + addMissingGroups, + standardsExcludeAllTenants, + }, + }) + } + className="mb-3 me-2" + > + Retry + + )} + {onboardingStatus.data?.Logs && ( + + )} +
    + {onboardingStatus.data?.OnboardingSteps?.map((step, idx) => ( + + + {step.Status == 'running' ? ( + + ) : ( + + )}{' '} + {step.Title} + + + {step.Message} + + + ))} + + )} +
    +
    + ) +} +RelationshipOnboarding.propTypes = { + relationship: PropTypes.object.isRequired, + gdapRoles: PropTypes.array, + autoMapRoles: PropTypes.bool, + addMissingGroups: PropTypes.bool, + statusOnly: PropTypes.bool, + standardsExcludeAllTenants: PropTypes.bool, +} + +export default RelationshipOnboarding diff --git a/src/views/tenant/standards/ListAppliedStandards.jsx b/src/views/tenant/standards/ListAppliedStandards.jsx index b5c8bc3c8174..484ec3862a36 100644 --- a/src/views/tenant/standards/ListAppliedStandards.jsx +++ b/src/views/tenant/standards/ListAppliedStandards.jsx @@ -39,6 +39,7 @@ import { CippTable, cellBooleanFormatter } from 'src/components/tables' import allStandardsList from 'src/data/standards' import CippCodeOffCanvas from 'src/components/utilities/CippCodeOffcanvas' import GDAPRoles from 'src/data/GDAPRoles' +import timezoneList from 'src/data/timezoneList' import Select from 'react-select' import { cellGenericFormatter } from 'src/components/tables/CellGenericFormat' @@ -73,6 +74,9 @@ const ApplyNewStandard = () => { const [templateStandard, setTemplateStandard] = useState() const [loadedTemplate, setLoadedTemplate] = useState(false) const [loadingTemplate, setLoadingTemplate] = useState(false) + const [enabledAlertsCount, setEnabledAlertsCount] = useState(0) + const [enabledRemediationsCount, setEnabledRemediationsCount] = useState(0) + const [enabledWarningsCount, setEnabledWarningsCount] = useState(0) const { data: listStandardTemplates = [], refetch: refetchStandardsTemplates } = useGenericGetRequestQuery({ @@ -258,12 +262,20 @@ const ApplyNewStandard = () => { }) const tenantDomain = useSelector((state) => state.app.currentTenant.defaultDomainName) - console.log('tenantDomain', tenantDomain) + //console.log('tenantDomain', tenantDomain) const [genericPostRequest, postResults] = useLazyGenericPostRequestQuery() const { data: listStandardsAllTenants = [] } = useGenericGetRequestQuery({ path: 'api/listStandards', }) + const { + data: consolidatedStandards = [], + isSuccess: consolidatedSuccess, + refetch: refetchConsolidated, + } = useGenericGetRequestQuery({ + path: 'api/listStandards', + params: { TenantFilter: tenantDomain, ShowConsolidated: true }, + }) const { data: listStandardResults = [], @@ -287,6 +299,9 @@ const ApplyNewStandard = () => { genericPostRequest({ path: '/api/AddStandardsDeploy', values: { ...values.standards, tenant: tenantDomain }, + }).then(() => { + refetchStandards() + refetchConsolidated() }) } const [intuneGetRequest, intuneTemplates] = useLazyGenericGetRequestQuery() @@ -298,16 +313,20 @@ const ApplyNewStandard = () => { (tenant) => tenant.displayName === 'AllTenants', ) - function getLabel(item, type) { + function isAllTenantEnabled(item, type) { if (!item || !item.name) { return '' } const keys = item.name.split('.') let value = keys.reduce((prev, curr) => prev && prev[curr], allTenantsStandard) - if (!value || !value[type]) { - return '' + if ( + !value || + !value[type] || + listStandardResults[0]?.standards?.OverrideAllTenants?.remediate === true + ) { + return false } - return `* Enabled via All Tenants` + return true } const groupedStandards = allStandardsList.reduce((acc, obj) => { @@ -316,29 +335,38 @@ const ApplyNewStandard = () => { return acc }, {}) - // Function to count enabled standards - function countEnabledStandards(standards, type) { - let count = 0 - Object.keys(standards).forEach((key) => { - const standard = standards[key] - // Check if 'Enabled' exists and the specific type is true, for non-v2 standards - if (standard?.Enabled && standard?.Enabled[type]) { - count++ - } else if (standard && standard[type]) { - // Check if the type exists directly under the standard - count++ - } - }) - return count - } - - // Assuming listStandardResults[0] contains your JSON object - const enabledStandards = listStandardResults[0] ? listStandardResults[0].standards : {} - const enabledAlertsCount = countEnabledStandards(enabledStandards, 'alert') - const enabledRemediationsCount = countEnabledStandards(enabledStandards, 'remediate') - const enabledWarningsCount = countEnabledStandards(enabledStandards, 'report') const totalAvailableStandards = allStandardsList.length + useEffect(() => { + if (consolidatedSuccess && consolidatedStandards.length > 0) { + var actions = ['alert', 'remediate', 'report'] + var enabledCounts = { + alert: 0, + remediate: 0, + report: 0, + } + consolidatedStandards.map((standard) => { + //console.log(standard.Standard) + if (standard?.Settings) { + actions.map((action) => { + if (standard?.Settings[action] === true) { + enabledCounts[action]++ + } + }) + } + }) + setEnabledAlertsCount(enabledCounts['alert']) + setEnabledRemediationsCount(enabledCounts['remediate']) + setEnabledWarningsCount(enabledCounts['report']) + } + }, [ + consolidatedStandards, + consolidatedSuccess, + setEnabledAlertsCount, + setEnabledRemediationsCount, + setEnabledWarningsCount, + ]) + return ( <> @@ -367,624 +395,676 @@ const ApplyNewStandard = () => { {getResults.error.message} )} - - - +
    +
    + + + 0 + ? Math.round((enabledWarningsCount / totalAvailableStandards) * 1000) / 10 + : 0, + }} + text={ + listStandardResults.length > 0 && listStandardResults[0].appliedBy + ? `Created by ${listStandardResults[0].appliedBy}` + : 'None' } - title={`List and edit standard - ${tenantDomain}`} - > - {isFetching && } - {intuneTemplates.isUninitialized && - intuneGetRequest({ path: 'api/ListIntuneTemplates' })} - {transportTemplates.isUninitialized && - transportGetRequest({ path: 'api/ListTransportRulesTemplates' })} - {caTemplates.isUninitialized && caGetRequest({ path: 'api/ListCAtemplates' })} - {exConnectorTemplates.isUninitialized && - exConnectorGetRequest({ path: 'api/ListExConnectorTemplates' })} - {groupTemplates.isUninitialized && - groupGetRequest({ path: 'api/ListGroupTemplates' })} - {isSuccess && !isFetching && ( - { - return ( - - - - 0 - ? Math.round( - (enabledWarningsCount / totalAvailableStandards) * 1000, - ) / 10 - : 0, - }} - text={ - listStandardResults[0].appliedBy - ? `Created by ${listStandardResults[0].appliedBy}` - : 'None' - } - title={`${enabledWarningsCount} out of ${totalAvailableStandards}`} - value="Enabled Warnings" - /> - - - 0 - ? Math.round( - (enabledAlertsCount / totalAvailableStandards) * 1000, - ) / 10 - : 0, - }} - text={ - listStandardResults[0].appliedBy - ? `Created by ${listStandardResults[0].appliedBy}` - : 'None' - } - title={`${enabledAlertsCount} out of ${totalAvailableStandards}`} - value="Enabled Alerts" - /> - - - 0 - ? Math.round( - (enabledRemediationsCount / totalAvailableStandards) * 1000, - ) / 10 - : 0, - }} - text={ - listStandardResults[0].appliedBy - ? `Created by ${listStandardResults[0].appliedBy}` - : 'None' - } - title={`${enabledRemediationsCount} out of ${totalAvailableStandards}`} - value="Enabled Remediations" - /> - - - {tenantDomain !== 'AllTenants' && ( - - General Standard Settings - - - -
    -
    Do not apply All Tenants Standard to this tenant
    -
    - Minimal Impact -
    -
    -

    - - Enabling this feature excludes this tenant from any - top-level "All Tenants" standard. This means that only the - standards you explicitly set for this tenant will be - applied. Standards previously applied by the "All Tenants" - standard will not be reverted. - -

    -
    - -
    Report
    - -
    - -
    Alert
    - -
    - -
    Remediate
    - -
    - -
    Optional Input
    -
    -
    -
    -
    - )} - {Object.keys(groupedStandards).map((cat, catIndex) => ( - - {cat} - - {groupedStandards[cat].map((obj, index) => ( - - -
    -
    {obj.label}
    -
    - {obj.impact} -
    -
    -

    - {obj.helpText} -

    -
    - -
    Report
    - -
    - -
    Alert
    - -
    - -
    Remediate
    - -
    - -
    Optional Input
    - {obj.addedComponent && - obj.addedComponent.map((component) => ( - <> - {component.type === 'Select' && ( - - )} - {component.type === 'input' && ( - - )} - {component.type === 'number' && ( - - )} - {component.type === 'boolean' && ( - - )} - {component.type === 'AdminRolesMultiSelect' && ( - ({ - value: role.ObjectId, - name: role.Name, - }))} - /> - )} - - ))} -
    -
    - ))} -
    -
    - ))} - - Templates Standard Deployment - - {[ - { - name: 'Intune Template', - switchName: 'standards.IntuneTemplate', - assignable: true, - templates: intuneTemplates, - }, - { - name: 'Transport Rule Template', - switchName: 'standards.TransportRuleTemplate', - templates: transportTemplates, - }, - { - name: 'Conditional Access Template', - switchName: 'standards.ConditionalAccess', - templates: caTemplates, - }, - { - name: 'Exchange Connector Template', - switchName: 'standards.ExConnector', - templates: exConnectorTemplates, - }, - { - name: 'Group Template', - switchName: 'standards.GroupTemplate', - templates: groupTemplates, - }, - ].map((template, index) => ( - - -
    {template.name}
    - Deploy {template.name} -
    - -
    Report
    - -
    - -
    Alert
    - -
    - -
    Remediate
    - -
    - -
    Optional Input
    - {template.templates.isSuccess && ( - ({ - value: t.GUID, - name: t.name || t.Displayname || t.displayName, - }))} - placeholder="Select a template" - label={`Choose your ${template.name}`} - /> - )} - {template.assignable && ( - <> - - - - - - - +
    + + 0 + ? Math.round((enabledAlertsCount / totalAvailableStandards) * 1000) / 10 + : 0, + }} + text={ + listStandardResults.length > 0 && listStandardResults[0].appliedBy + ? `Created by ${listStandardResults[0].appliedBy}` + : 'None' + } + title={`${enabledAlertsCount} out of ${totalAvailableStandards}`} + value="Enabled Alerts" + /> + + + 0 + ? Math.round((enabledRemediationsCount / totalAvailableStandards) * 1000) / 10 + : 0, + }} + text={ + listStandardResults.length > 0 && listStandardResults[0].appliedBy + ? `Created by ${listStandardResults[0].appliedBy}` + : 'None' + } + title={`${enabledRemediationsCount} out of ${totalAvailableStandards}`} + value="Enabled Remediations" + /> + +
    + + + + } + title={`List and edit standard - ${tenantDomain}`} + className="mb-3" + > + {isFetching && } + {intuneTemplates.isUninitialized && intuneGetRequest({ path: 'api/ListIntuneTemplates' })} + {transportTemplates.isUninitialized && + transportGetRequest({ path: 'api/ListTransportRulesTemplates' })} + {caTemplates.isUninitialized && caGetRequest({ path: 'api/ListCAtemplates' })} + {exConnectorTemplates.isUninitialized && + exConnectorGetRequest({ path: 'api/ListExConnectorTemplates' })} + {groupTemplates.isUninitialized && groupGetRequest({ path: 'api/ListGroupTemplates' })} + {isSuccess && !isFetching && ( + { + return ( + + + + {tenantDomain !== 'AllTenants' && ( + + General Standard Settings + + + +
    +
    Do not apply All Tenants Standard to this tenant
    +
    + Minimal Impact +
    +
    +

    + + Enabling this feature excludes this tenant from any top-level + "All Tenants" standard. This means that only the standards you + explicitly set for this tenant will be applied. Standards + previously applied by the "All Tenants" standard will not be + reverted. + +

    +
    + +
    Report
    + +
    + +
    Alert
    + +
    + +
    Remediate
    + +
    + +
    Optional Input
    +
    +
    +
    +
    + )} + + {/* eslint-disable react/prop-types */} + {(props) => { + return ( + <> + {Object.keys(groupedStandards).map((cat, catIndex) => ( + + {cat} + + {groupedStandards[cat].map((obj, index) => ( + + +
    +
    {obj.label}
    +
    + + {obj.impact} + +
    +
    +

    + {obj.helpText} +

    +
    + +
    Report
    + - - - )} -
    -
    +
    + +
    Alert
    + +
    + +
    Remediate
    + +
    + +
    Optional Input
    + {obj.addedComponent && + obj.addedComponent.map((component) => ( + <> + {component.type === 'Select' && ( + + )} + {component.type === 'input' && ( + + )} + {component.type === 'number' && ( + + )} + {component.type === 'boolean' && ( + + )} + {component.type === 'AdminRolesMultiSelect' && ( + ({ + value: role.ObjectId, + name: role.Name, + }))} + /> + )} + {component.type === 'TimezoneSelect' && ( + ({ + value: tz.timezone, + name: tz.timezone, + }))} + /> + )} + + ))} +
    +
    + ))} + + ))} - - -
    Autopilot Profile
    - Deploy Autopilot profile -
    - -
    Report
    - -
    - -
    Alert
    - -
    - -
    Remediate
    - -
    - -
    Optional Input
    - - - - - - - - - - - - + + ) + }} + + + Templates Standard Deployment + + {[ + { + name: 'Intune Template', + switchName: 'standards.IntuneTemplate', + assignable: true, + templates: intuneTemplates, + }, + { + name: 'Transport Rule Template', + switchName: 'standards.TransportRuleTemplate', + templates: transportTemplates, + }, + { + name: 'Conditional Access Template', + switchName: 'standards.ConditionalAccess', + templates: caTemplates, + }, + { + name: 'Exchange Connector Template', + switchName: 'standards.ExConnector', + templates: exConnectorTemplates, + }, + { + name: 'Group Template', + switchName: 'standards.GroupTemplate', + templates: groupTemplates, + }, + ].map((template, index) => ( + + +
    {template.name}
    + Deploy {template.name} +
    + +
    Report
    + +
    + +
    Alert
    + +
    + +
    Remediate
    + +
    + +
    Optional Input
    + {template.templates.isSuccess && ( + ({ + value: t.GUID, + name: t.name || t.Displayname || t.displayName, + }))} + placeholder="Select a template" + label={`Choose your ${template.name}`} + /> + )} + {template.assignable && ( + <> + + + + + + -

    -
    -
    - - - - - - - - - + + )} +
    +
    + ))} + + +
    Autopilot Profile
    + Deploy Autopilot profile +
    + +
    Report
    + +
    + +
    Alert
    + +
    + +
    Remediate
    + +
    + +
    Optional Input
    + + + - - -
    Autopilot Status Page
    - Deploy Autopilot Status Page -
    - -
    Report
    - + + + - -
    Alert
    - +
    + + + +

    +
    + + + + + + + + + +
    +
    + + +
    Autopilot Status Page
    + Deploy Autopilot Status Page +
    + +
    Report
    + +
    + +
    Alert
    + +
    + +
    Remediate
    + +
    + +
    Optional Input
    + -
    Remediate
    - -
    - -
    Optional Input
    - - - - - - - - - - - - - - - - - +
    + + + - - - -
    + + + + + + + +
    +
    + + + +
    - - {postResults.isSuccess && ( - {postResults.data.Results} - )} - - - - Save - {postResults.isFetching && ( - - )} - - - - {listStandardResults[0].appliedBy && ( - - )} - - {/* eslint-disable react/prop-types */} - {(props) => { - return ( - <> - templateSave(props.values)} - disabled={submitting} - > - {execSaveResults.isFetching && ( - - )} - {execSaveResults.error && ( - - )} - {execSaveResults.isSuccess && ( - - )} - Save as template - - - ) - }} - - - - - - ) - }} - /> - )} - - -
    + + {postResults.isSuccess && ( + {postResults.data.Results} + )} + + + + Save + {postResults.isFetching && ( + + )} + + + + {listStandardResults[0].appliedBy && } + + {/* eslint-disable react/prop-types */} + {(props) => { + return ( + <> + templateSave(props.values)} + disabled={submitting} + > + {execSaveResults.isFetching && ( + + )} + {execSaveResults.error && ( + + )} + {execSaveResults.isSuccess && ( + + )} + Save as template + + + ) + }} + + + + + + ) + }} + /> + )} + ) diff --git a/version_latest.txt b/version_latest.txt index b7c75422bc1d..42cdd0b540f9 100644 --- a/version_latest.txt +++ b/version_latest.txt @@ -1 +1 @@ -5.6.1 +5.7.0