From 87ae2ea7c29d4aed1e144859bad389f576338048 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Fri, 12 Apr 2024 14:39:52 -0400 Subject: [PATCH 001/475] Update SettingsPartner.jsx --- src/views/cipp/app-settings/SettingsPartner.jsx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/views/cipp/app-settings/SettingsPartner.jsx b/src/views/cipp/app-settings/SettingsPartner.jsx index 91f45d98c11e..388e2a6a6e9d 100644 --- a/src/views/cipp/app-settings/SettingsPartner.jsx +++ b/src/views/cipp/app-settings/SettingsPartner.jsx @@ -208,6 +208,13 @@ export function SettingsPartner() { Waiting for results )} + {sendTestResult.isSuccess && sendTestResult?.data?.Results?.code && ( + <> + Error{' '} + {sendTestResult?.data?.Results?.code} -{' '} + {sendTestResult?.data?.Results?.description} + + )} From b8e0e3338aecb01783e486fe030a88868df6d6e7 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Sun, 14 Apr 2024 23:06:50 +0200 Subject: [PATCH 002/475] Test automatic retries --- src/store/api/baseQuery.js | 61 ++++++++++++++++++++++++-------------- 1 file changed, 38 insertions(+), 23 deletions(-) diff --git a/src/store/api/baseQuery.js b/src/store/api/baseQuery.js index 493b6d012bab..6ca47874842b 100644 --- a/src/store/api/baseQuery.js +++ b/src/store/api/baseQuery.js @@ -1,30 +1,45 @@ import axios from 'axios' -let newController = new AbortController() + +const retryDelays = [1000, 2000, 3000] // Delays in milliseconds for retries + export const axiosQuery = async ({ path, method = 'get', params, data, hideToast }) => { - try { - const result = await axios({ - signal: path === '/api/ListTenants' ? undefined : newController.signal, - method, - baseURL: window.location.origin, - url: path, - data, - params, - }) - return { data: result?.data } - } catch (error) { - return { - error: { - status: error.response?.status, - data: error.response?.data, - hideToast, - message: error?.message, - }, + let attempt = 0 + + while (attempt <= retryDelays.length) { + try { + const result = await axios({ + method, + baseURL: window.location.origin, + url: path, + data, + params, + }) + return { data: result.data } // Successful response + } catch (error) { + if (attempt === retryDelays.length || !shouldRetry(error, path)) { + return { + // Max retries reached or error should not trigger a retry + error: { + status: error.response?.status, + data: error.response?.data, + hideToast, + message: error.message, + }, + } + } + await delay(retryDelays[attempt]) // Wait before retrying + attempt++ } } } -export function abortRequestSafe() { - newController.abort() - newController = new AbortController() + +const shouldRetry = (error, path) => { + // Check if the path starts with 'List', error qualifies for a retry, and payload message is 'Backend call failure' + return ( + path.startsWith('/List') && + (!error.response || error.response.status >= 500) && + error.response?.data === 'Backend call failure' + ) } -export const baseQuery = ({ baseUrl } = { baseUrl: '' }) => axiosQuery +const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms)) From 5a13d28cb48f4a7adcab6650541c28738cd32db6 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Sun, 14 Apr 2024 23:12:33 +0200 Subject: [PATCH 003/475] case sensitivity --- src/store/api/baseQuery.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/store/api/baseQuery.js b/src/store/api/baseQuery.js index 6ca47874842b..c807fe4a35c3 100644 --- a/src/store/api/baseQuery.js +++ b/src/store/api/baseQuery.js @@ -36,7 +36,7 @@ export const axiosQuery = async ({ path, method = 'get', params, data, hideToast const shouldRetry = (error, path) => { // Check if the path starts with 'List', error qualifies for a retry, and payload message is 'Backend call failure' return ( - path.startsWith('/List') && + path.toLower().startsWith('/list') && (!error.response || error.response.status >= 500) && error.response?.data === 'Backend call failure' ) From 957562685070ad5668f9b03a23f8713dba97d798 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Sun, 14 Apr 2024 23:20:16 +0200 Subject: [PATCH 004/475] return export --- src/store/api/baseQuery.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/store/api/baseQuery.js b/src/store/api/baseQuery.js index c807fe4a35c3..b9e730e9300a 100644 --- a/src/store/api/baseQuery.js +++ b/src/store/api/baseQuery.js @@ -43,3 +43,5 @@ const shouldRetry = (error, path) => { } const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms)) + +export const baseQuery = ({ baseUrl } = { baseUrl: '' }) => axiosQuery From 5502c47c8db417d879f4f40b357992bb393b3841 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Sun, 14 Apr 2024 23:25:58 +0200 Subject: [PATCH 005/475] returned abort request --- src/store/api/baseQuery.js | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/store/api/baseQuery.js b/src/store/api/baseQuery.js index b9e730e9300a..23291e826afb 100644 --- a/src/store/api/baseQuery.js +++ b/src/store/api/baseQuery.js @@ -1,5 +1,7 @@ import axios from 'axios' +let newController = new AbortController() // Controller for managing abortion of requests + const retryDelays = [1000, 2000, 3000] // Delays in milliseconds for retries export const axiosQuery = async ({ path, method = 'get', params, data, hideToast }) => { @@ -8,6 +10,7 @@ export const axiosQuery = async ({ path, method = 'get', params, data, hideToast while (attempt <= retryDelays.length) { try { const result = await axios({ + signal: newController.signal, method, baseURL: window.location.origin, url: path, @@ -36,12 +39,18 @@ export const axiosQuery = async ({ path, method = 'get', params, data, hideToast const shouldRetry = (error, path) => { // Check if the path starts with 'List', error qualifies for a retry, and payload message is 'Backend call failure' return ( - path.toLower().startsWith('/list') && - (!error.response || error.response.status >= 500) && - error.response?.data === 'Backend call failure' + path.startsWith('/List') && + error.response && + error.response.status >= 500 && + error.response.data === 'Backend call failure' ) } const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms)) +export function abortRequestSafe() { + newController.abort() // Abort any ongoing request + newController = new AbortController() // Reset the controller for new requests +} + export const baseQuery = ({ baseUrl } = { baseUrl: '' }) => axiosQuery From c36b6c3544748d82cb08f390803943f839f8c2a8 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Sun, 14 Apr 2024 23:36:31 +0200 Subject: [PATCH 006/475] casing --- src/store/api/baseQuery.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/store/api/baseQuery.js b/src/store/api/baseQuery.js index 23291e826afb..caa1d857d58a 100644 --- a/src/store/api/baseQuery.js +++ b/src/store/api/baseQuery.js @@ -39,7 +39,7 @@ export const axiosQuery = async ({ path, method = 'get', params, data, hideToast const shouldRetry = (error, path) => { // Check if the path starts with 'List', error qualifies for a retry, and payload message is 'Backend call failure' return ( - path.startsWith('/List') && + path.startsWith('/list') && error.response && error.response.status >= 500 && error.response.data === 'Backend call failure' From fd2da909767e7d63125f111d65f916b8695b27de Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Sun, 14 Apr 2024 23:37:14 +0200 Subject: [PATCH 007/475] console logging --- src/store/api/baseQuery.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/store/api/baseQuery.js b/src/store/api/baseQuery.js index caa1d857d58a..24fa927e6bfb 100644 --- a/src/store/api/baseQuery.js +++ b/src/store/api/baseQuery.js @@ -39,10 +39,11 @@ export const axiosQuery = async ({ path, method = 'get', params, data, hideToast const shouldRetry = (error, path) => { // Check if the path starts with 'List', error qualifies for a retry, and payload message is 'Backend call failure' return ( + console.log(path), path.startsWith('/list') && - error.response && - error.response.status >= 500 && - error.response.data === 'Backend call failure' + error.response && + error.response.status >= 500 && + error.response.data === 'Backend call failure' ) } From c23ded7fa6660765fc566f64fad741401f7b6498 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Sun, 14 Apr 2024 23:37:23 +0200 Subject: [PATCH 008/475] api --- src/store/api/baseQuery.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/store/api/baseQuery.js b/src/store/api/baseQuery.js index 24fa927e6bfb..d2fa96e5d668 100644 --- a/src/store/api/baseQuery.js +++ b/src/store/api/baseQuery.js @@ -40,7 +40,7 @@ const shouldRetry = (error, path) => { // Check if the path starts with 'List', error qualifies for a retry, and payload message is 'Backend call failure' return ( console.log(path), - path.startsWith('/list') && + path.startsWith('api/list') && error.response && error.response.status >= 500 && error.response.data === 'Backend call failure' From 136d9cf2fd1e5abf8c0621a59fa17b37ffba206e Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Sun, 14 Apr 2024 23:43:41 +0200 Subject: [PATCH 009/475] console logs --- src/App.jsx | 4 ++-- src/views/cipp/app-settings/SettingsGeneral.jsx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/App.jsx b/src/App.jsx index 5e5678b80784..276f9dcc21a5 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -55,8 +55,8 @@ const App = () => { {routes.map((route, idx) => { const allowedRoles = route.allowedRoles const Routecomponent = dynamicImport(route.path) - console.log('route', route) - console.log('Routecomponent', Routecomponent) + //console.log('route', route) + //console.log('Routecomponent', Routecomponent) return ( route.component && ( { - console.log(e) + //console.log(e) setShowExtendedInfo(!e.target.checked) }} key={'Show Extended Info'} From ef830dcefc9072a9305ff1ffabd83e2ba01dbbc8 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Mon, 15 Apr 2024 00:01:17 +0200 Subject: [PATCH 010/475] tolower --- src/store/api/baseQuery.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/store/api/baseQuery.js b/src/store/api/baseQuery.js index d2fa96e5d668..cc1052b7f7af 100644 --- a/src/store/api/baseQuery.js +++ b/src/store/api/baseQuery.js @@ -19,6 +19,8 @@ export const axiosQuery = async ({ path, method = 'get', params, data, hideToast }) return { data: result.data } // Successful response } catch (error) { + console.log('error', error) + console.log('path', path) if (attempt === retryDelays.length || !shouldRetry(error, path)) { return { // Max retries reached or error should not trigger a retry @@ -39,14 +41,12 @@ export const axiosQuery = async ({ path, method = 'get', params, data, hideToast const shouldRetry = (error, path) => { // Check if the path starts with 'List', error qualifies for a retry, and payload message is 'Backend call failure' return ( - console.log(path), - path.startsWith('api/list') && - error.response && - error.response.status >= 500 && - error.response.data === 'Backend call failure' + path.toLower().startsWith('api/list') && + error.response && + error.response.status >= 500 && + error.response.data === 'Backend call failure' ) } - const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms)) export function abortRequestSafe() { From d4684841f3df1ea1906afeb52c84596a2a15e7b4 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Mon, 15 Apr 2024 00:01:55 +0200 Subject: [PATCH 011/475] lowercase --- src/store/api/baseQuery.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/store/api/baseQuery.js b/src/store/api/baseQuery.js index cc1052b7f7af..6c723b74aee4 100644 --- a/src/store/api/baseQuery.js +++ b/src/store/api/baseQuery.js @@ -41,7 +41,7 @@ export const axiosQuery = async ({ path, method = 'get', params, data, hideToast const shouldRetry = (error, path) => { // Check if the path starts with 'List', error qualifies for a retry, and payload message is 'Backend call failure' return ( - path.toLower().startsWith('api/list') && + path.toLowerCase().startsWith('api/list') && error.response && error.response.status >= 500 && error.response.data === 'Backend call failure' From 5c77a19c9e6597564117290c86d2207186bda9b2 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Mon, 15 Apr 2024 00:11:19 +0200 Subject: [PATCH 012/475] improvements to retry --- src/store/api/baseQuery.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/store/api/baseQuery.js b/src/store/api/baseQuery.js index 6c723b74aee4..896401eccb83 100644 --- a/src/store/api/baseQuery.js +++ b/src/store/api/baseQuery.js @@ -2,7 +2,7 @@ import axios from 'axios' let newController = new AbortController() // Controller for managing abortion of requests -const retryDelays = [1000, 2000, 3000] // Delays in milliseconds for retries +const retryDelays = [100, 200, 300] // Delays in milliseconds for retries export const axiosQuery = async ({ path, method = 'get', params, data, hideToast }) => { let attempt = 0 @@ -41,7 +41,7 @@ export const axiosQuery = async ({ path, method = 'get', params, data, hideToast const shouldRetry = (error, path) => { // Check if the path starts with 'List', error qualifies for a retry, and payload message is 'Backend call failure' return ( - path.toLowerCase().startsWith('api/list') && + path.toLowerCase().startsWith('/api/list') && error.response && error.response.status >= 500 && error.response.data === 'Backend call failure' From 5799b41728514d5cff253af6721ca000c0a0151d Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Mon, 15 Apr 2024 16:33:29 +0200 Subject: [PATCH 013/475] remove blank items from favourites. --- src/views/cipp/UserSettings.jsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/views/cipp/UserSettings.jsx b/src/views/cipp/UserSettings.jsx index 955101c02921..8a4081b8c29b 100644 --- a/src/views/cipp/UserSettings.jsx +++ b/src/views/cipp/UserSettings.jsx @@ -216,6 +216,8 @@ const UserSettings = () => { multi={true} values={_nav .reduce((acc, val) => acc.concat(val.items), []) + //only map if 'name' property is not null + .filter((item) => item?.name) .map((item) => // console.log(item), ({ From 128d98b4080c4cf8d1f6f63f448a0f0d409e9b82 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Mon, 15 Apr 2024 16:59:55 +0200 Subject: [PATCH 014/475] add tab browsing --- src/views/cipp/app-settings/CIPPSettings.jsx | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/views/cipp/app-settings/CIPPSettings.jsx b/src/views/cipp/app-settings/CIPPSettings.jsx index c1e6b9463fd7..6e45b3ba5780 100644 --- a/src/views/cipp/app-settings/CIPPSettings.jsx +++ b/src/views/cipp/app-settings/CIPPSettings.jsx @@ -2,7 +2,7 @@ import React, { useState } from 'react' import { CNav, CNavItem, CTabContent, CTabPane } from '@coreui/react' import { CippPage } from 'src/components/layout' import { CippLazy } from 'src/components/utilities' - +import { useNavigate } from 'react-router-dom' import { SettingsGeneral } from './SettingsGeneral.jsx' import { SettingsTenants } from 'src/views/cipp/app-settings/SettingsTenants.jsx' import { SettingsBackend } from 'src/views/cipp/app-settings/SettingsBackend.jsx' @@ -12,6 +12,7 @@ import { SettingsExtensions } from 'src/views/cipp/app-settings/SettingsExtensio import { SettingsMaintenance } from 'src/views/cipp/app-settings/SettingsMaintenance.jsx' import { SettingsExtensionMappings } from 'src/views/cipp/app-settings/SettingsExtensionMappings.jsx' import { SettingsPartner } from 'src/views/cipp/app-settings/SettingsPartner.jsx' +import useQuery from 'src/hooks/useQuery.jsx' /** * This function returns the settings page content for CIPP. @@ -19,7 +20,18 @@ import { SettingsPartner } from 'src/views/cipp/app-settings/SettingsPartner.jsx * @returns {JSX.Element} The settings page content. */ export default function CIPPSettings() { - const [active, setActive] = useState(1) + const queryString = useQuery() + const navigate = useNavigate() + + const tab = queryString.get('tab') + const [active, setActiveTab] = useState(tab ? parseInt(tab) : 1) + + const setActive = (tab) => { + setActiveTab(tab) + queryString.set('tab', tab.toString()) + navigate(`${location.pathname}?${queryString}`) + } + return ( From d184d12388778631d5c3881614afe8442ef6bca8 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Mon, 15 Apr 2024 17:14:51 +0200 Subject: [PATCH 015/475] added refresh to list tenants. --- src/components/utilities/TenantSelector.jsx | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/components/utilities/TenantSelector.jsx b/src/components/utilities/TenantSelector.jsx index a7c059c39d4f..df8eb7d00459 100644 --- a/src/components/utilities/TenantSelector.jsx +++ b/src/components/utilities/TenantSelector.jsx @@ -13,6 +13,7 @@ import CippTenantOffcanvas from './CippTenantOffcanvas' import CippfuzzySearch from './CippFuzzySearch' const TenantSelector = ({ action, showAllTenantSelector = true, NavSelector = false }) => { + const [refreshState, setRefreshState] = React.useState(false) const currentTenant = useSelector((state) => state.app.currentTenant) const { data: tenants = [ @@ -23,9 +24,10 @@ const TenantSelector = ({ action, showAllTenantSelector = true, NavSelector = fa }, ], isLoading, + isFetching, isSuccess, error, - } = useListTenantsQuery({ showAllTenantSelector }) + } = useListTenantsQuery({ showAllTenantSelector, Refresh: refreshState }) const dispatch = useDispatch() const navigate = useNavigate() @@ -90,10 +92,10 @@ const TenantSelector = ({ action, showAllTenantSelector = true, NavSelector = fa )} -
+
+ + //set a random number to force a refresh + setRefreshState(Math.random()) + } + variant="ghost" + className="ml-2" + > + +
)} From 450858ff7a44c6a42ab7bb922e1f1646fb366b63 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Mon, 15 Apr 2024 17:28:43 +0200 Subject: [PATCH 016/475] fixes or/ands --- src/components/tables/CippTable.jsx | 74 ++++++++++++++--------------- 1 file changed, 36 insertions(+), 38 deletions(-) diff --git a/src/components/tables/CippTable.jsx b/src/components/tables/CippTable.jsx index 510aaa3ec9f0..dbcde884e90d 100644 --- a/src/components/tables/CippTable.jsx +++ b/src/components/tables/CippTable.jsx @@ -277,47 +277,45 @@ export default function CippTable({ debounceSetGraphFilter(query) return data } else if (filterText.startsWith('Complex:')) { - // Split conditions by ';' and 'or', and trim spaces - const conditions = filterText + // Split conditions by ';' for AND + const conditionGroups = filterText .slice(9) - .split(/\s*or\s*|\s*;\s*/i) // Split by 'or' or ';', case insensitive, with optional spaces - .map((condition) => condition.trim()) + .split(/\s*;\s*/) + .map((group) => group.trim().split(/\s+or\s+/i)) // Split each group by 'or' for OR return data.filter((item) => { - // Check if any condition is met for the item - return conditions.some((condition) => { - const match = condition.match(/(\w+)\s*(eq|ne|like|notlike|gt|lt)\s*(.+)/) - - if (!match) return false - - let [property, operator, value] = match.slice(1) - value = escapeRegExp(value) // Escape special characters - - const actualKey = Object.keys(item).find( - (key) => key.toLowerCase() === property.toLowerCase(), - ) - - if (!actualKey) { - console.error(`FilterError: Property "${property}" not found.`) - return false - } - - switch (operator) { - case 'eq': - return String(item[actualKey]).toLowerCase() === value.toLowerCase() - case 'ne': - return String(item[actualKey]).toLowerCase() !== value.toLowerCase() - case 'like': - return String(item[actualKey]).toLowerCase().includes(value.toLowerCase()) - case 'notlike': - return !String(item[actualKey]).toLowerCase().includes(value.toLowerCase()) - case 'gt': - return parseFloat(item[actualKey]) > parseFloat(value) - case 'lt': - return parseFloat(item[actualKey]) < parseFloat(value) - default: - return false // Should not reach here normally - } + // Check if all condition groups are met for the item (AND logic) + return conditionGroups.every((conditions) => { + // Check if any condition within a group is met for the item (OR logic) + return conditions.some((condition) => { + const match = condition.match(/(\w+)\s*(eq|ne|like|notlike|gt|lt)\s*(.+)/) + if (!match) return false + let [property, operator, value] = match.slice(1) + value = escapeRegExp(value) // Escape special characters + const actualKey = Object.keys(item).find( + (key) => key.toLowerCase() === property.toLowerCase(), + ) + if (!actualKey) { + console.error(`FilterError: Property "${property}" not found.`) + return false + } + switch (operator) { + case 'eq': + return String(item[actualKey]).toLowerCase() === value.toLowerCase() + case 'ne': + return String(item[actualKey]).toLowerCase() !== value.toLowerCase() + case 'like': + return String(item[actualKey]).toLowerCase().includes(value.toLowerCase()) + case 'notlike': + return !String(item[actualKey]).toLowerCase().includes(value.toLowerCase()) + case 'gt': + return parseFloat(item[actualKey]) > parseFloat(value) + case 'lt': + return parseFloat(item[actualKey]) < parseFloat(value) + default: + return false // Should not reach here normally + } + }) }) }) } else { From f7bdf2d39f8a79b200653568c87d1d85560dbaf6 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Mon, 15 Apr 2024 23:18:59 +0200 Subject: [PATCH 017/475] fix bpa --- src/components/tables/CellTable.jsx | 1 - src/components/utilities/Toasts.jsx | 16 +++--- src/store/middleware/errorMiddleware.js | 2 +- .../tenant/standards/BestPracticeAnalyser.jsx | 50 +++++++++++++++---- 4 files changed, 52 insertions(+), 17 deletions(-) diff --git a/src/components/tables/CellTable.jsx b/src/components/tables/CellTable.jsx index 8c259bc06200..0fffd6da5637 100644 --- a/src/components/tables/CellTable.jsx +++ b/src/components/tables/CellTable.jsx @@ -39,7 +39,6 @@ export default function cellTable( const handleTable = ({ columnProp }) => { const QueryColumns = [] - const columns = Object.keys(columnProp[0]).map((key) => { QueryColumns.push({ name: key, diff --git a/src/components/utilities/Toasts.jsx b/src/components/utilities/Toasts.jsx index cfc6d99c3b4d..e06bc398d5c7 100644 --- a/src/components/utilities/Toasts.jsx +++ b/src/components/utilities/Toasts.jsx @@ -3,8 +3,9 @@ import PropTypes from 'prop-types' import { useDispatch, useSelector } from 'react-redux' import { CToast, CToastBody, CToaster, CToastHeader, CCollapse, CButton } from '@coreui/react' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' -import { faExpandAlt, faCompressAlt, faTimes } from '@fortawesome/free-solid-svg-icons' +import { faTimes } from '@fortawesome/free-solid-svg-icons' import { closeToast } from 'src/store/features/toasts' +import ReactTimeAgo from 'react-time-ago' const Toasts = () => { const dispatch = useDispatch() @@ -32,15 +33,18 @@ const Toast = ({ message, title, onClose, error }) => { return ( - -
{title}
- + +
{title}
+ Just Now +
+ +
diff --git a/src/store/middleware/errorMiddleware.js b/src/store/middleware/errorMiddleware.js index d09c48915ff3..14e868882709 100644 --- a/src/store/middleware/errorMiddleware.js +++ b/src/store/middleware/errorMiddleware.js @@ -39,7 +39,7 @@ export const errorMiddleware = dispatch( showToast({ - title: 'An Error Has Occurred', + title: 'An error has occurred', message: message, toastError, }), diff --git a/src/views/tenant/standards/BestPracticeAnalyser.jsx b/src/views/tenant/standards/BestPracticeAnalyser.jsx index 3c94d28db3fd..10e7c480c29f 100644 --- a/src/views/tenant/standards/BestPracticeAnalyser.jsx +++ b/src/views/tenant/standards/BestPracticeAnalyser.jsx @@ -164,7 +164,17 @@ const BestPracticeAnalyser = () => { }, ], } - + const normalizeTableData = (value) => { + if (Array.isArray(value)) { + return value + } else if (value === null) { + return null + } else if (typeof value === 'object') { + return [value] + } else { + return value + } + } if (graphrequest.isSuccess) { if (graphrequest.data.length === 0) { graphrequest.data = [{ data: 'No Data Found' }] @@ -384,14 +394,36 @@ const BestPracticeAnalyser = () => { )} {info.formatter === 'table' && ( - + <> + + )} {info.formatter === 'number' && ( From bb6c7bd0f9e42eef79c6b391fb6e038a19c2ff5d Mon Sep 17 00:00:00 2001 From: Woody Date: Mon, 15 Apr 2024 21:30:56 -0400 Subject: [PATCH 018/475] Added Tenant ID to Tenant Information Offcanvas --- src/components/utilities/CippTenantOffcanvas.jsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/components/utilities/CippTenantOffcanvas.jsx b/src/components/utilities/CippTenantOffcanvas.jsx index 0538ecbf4344..dc9310d7aa81 100644 --- a/src/components/utilities/CippTenantOffcanvas.jsx +++ b/src/components/utilities/CippTenantOffcanvas.jsx @@ -58,6 +58,10 @@ function CippTenantOffcanvas({ tenant, buildingIcon = false }) { label: 'Display Name', value: tenantProperty(tenantDetails, 'displayName'), }, + { + label: 'Tenant ID', + value: tenantProperty(tenantDetails, 'id'), + }, { label: 'Business Phones', value: tenantProperty(tenantDetails, 'businessPhones'), From eeee870f0e262b9ae9557405d5346bc88f948afd Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Tue, 16 Apr 2024 20:33:26 +0200 Subject: [PATCH 019/475] add superadmin compare --- src/hooks/useRouteNavCompare.jsx | 43 ++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/src/hooks/useRouteNavCompare.jsx b/src/hooks/useRouteNavCompare.jsx index 27fc1f9a3a14..7d122ef5d927 100644 --- a/src/hooks/useRouteNavCompare.jsx +++ b/src/hooks/useRouteNavCompare.jsx @@ -6,28 +6,33 @@ import routes from 'src/routes' export const useRouteNavCompare = (navigation) => { const dispatch = useDispatch() const { data: profile, isFetching } = useLoadClientPrincipalQuery() + if (isFetching) { return { isLoading: true, component: null } } + dispatch(updateAccessToken(profile)) - let roles = profile?.clientPrincipal?.userRoles || [] - let newNavigation = navigation.map((nav) => { - if (nav.items) { - nav.items = nav.items.filter((item) => { - const route = routes.find((r) => r.path === item.to) - if ( - !route || - (route.allowedRoles && route.allowedRoles.some((role) => roles.includes(role))) - ) { - return true - } else { - //console.log('Removing route', item) - return false - } - }) - } - return nav - }) + const roles = profile?.clientPrincipal?.userRoles || [] + + if (roles.includes('superadmin')) { + // For 'superadmin', simplify to Dashboard and /cipp/ routes directly so people don't work under this account. + return navigation.filter((nav) => nav.to === '/home' || nav.to?.startsWith('/cipp')) + } - return newNavigation + // For other roles, use existing filtering logic + return navigation + .map((nav) => { + if (nav.items) { + nav.items = nav.items.filter((item) => { + const route = routes.find((r) => r.path === item.to) + return ( + route && + (!route.allowedRoles || route.allowedRoles.some((role) => roles.includes(role))) + ) + }) + return nav + } + return nav + }) + .filter((nav) => nav.items && nav.items.length > 0) // Remove empty navigation groups } From 894c017c0b819258416e91848294be943535d361 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Tue, 16 Apr 2024 22:05:19 +0200 Subject: [PATCH 020/475] added easier way of setting up multitenant/partner/singletenant mode. --- src/hooks/useRouteNavCompare.jsx | 43 +++---- src/views/cipp/app-settings/CIPPSettings.jsx | 16 ++- .../cipp/app-settings/SettingsSuperAdmin.jsx | 113 ++++++++++++++++++ 3 files changed, 146 insertions(+), 26 deletions(-) create mode 100644 src/views/cipp/app-settings/SettingsSuperAdmin.jsx diff --git a/src/hooks/useRouteNavCompare.jsx b/src/hooks/useRouteNavCompare.jsx index 7d122ef5d927..27fc1f9a3a14 100644 --- a/src/hooks/useRouteNavCompare.jsx +++ b/src/hooks/useRouteNavCompare.jsx @@ -6,33 +6,28 @@ import routes from 'src/routes' export const useRouteNavCompare = (navigation) => { const dispatch = useDispatch() const { data: profile, isFetching } = useLoadClientPrincipalQuery() - if (isFetching) { return { isLoading: true, component: null } } - dispatch(updateAccessToken(profile)) - const roles = profile?.clientPrincipal?.userRoles || [] - - if (roles.includes('superadmin')) { - // For 'superadmin', simplify to Dashboard and /cipp/ routes directly so people don't work under this account. - return navigation.filter((nav) => nav.to === '/home' || nav.to?.startsWith('/cipp')) - } + let roles = profile?.clientPrincipal?.userRoles || [] + let newNavigation = navigation.map((nav) => { + if (nav.items) { + nav.items = nav.items.filter((item) => { + const route = routes.find((r) => r.path === item.to) + if ( + !route || + (route.allowedRoles && route.allowedRoles.some((role) => roles.includes(role))) + ) { + return true + } else { + //console.log('Removing route', item) + return false + } + }) + } + return nav + }) - // For other roles, use existing filtering logic - return navigation - .map((nav) => { - if (nav.items) { - nav.items = nav.items.filter((item) => { - const route = routes.find((r) => r.path === item.to) - return ( - route && - (!route.allowedRoles || route.allowedRoles.some((role) => roles.includes(role))) - ) - }) - return nav - } - return nav - }) - .filter((nav) => nav.items && nav.items.length > 0) // Remove empty navigation groups + return newNavigation } diff --git a/src/views/cipp/app-settings/CIPPSettings.jsx b/src/views/cipp/app-settings/CIPPSettings.jsx index 6e45b3ba5780..965e1f98f11f 100644 --- a/src/views/cipp/app-settings/CIPPSettings.jsx +++ b/src/views/cipp/app-settings/CIPPSettings.jsx @@ -13,6 +13,8 @@ import { SettingsMaintenance } from 'src/views/cipp/app-settings/SettingsMainten import { SettingsExtensionMappings } from 'src/views/cipp/app-settings/SettingsExtensionMappings.jsx' import { SettingsPartner } from 'src/views/cipp/app-settings/SettingsPartner.jsx' import useQuery from 'src/hooks/useQuery.jsx' +import { SettingsSuperAdmin } from './SettingsSuperAdmin.jsx' +import { useLoadClientPrincipalQuery } from 'src/store/api/auth.js' /** * This function returns the settings page content for CIPP. @@ -25,13 +27,13 @@ export default function CIPPSettings() { const tab = queryString.get('tab') const [active, setActiveTab] = useState(tab ? parseInt(tab) : 1) - + const { data: profile, isFetching } = useLoadClientPrincipalQuery() const setActive = (tab) => { setActiveTab(tab) queryString.set('tab', tab.toString()) navigate(`${location.pathname}?${queryString}`) } - + const superAdmin = profile?.clientPrincipal?.userRoles?.includes('superadmin') return ( @@ -62,6 +64,11 @@ export default function CIPPSettings() { setActive(9)} href="#"> Extension Mappings + {superAdmin && ( + setActive(10)} href="#"> + SuperAdmin Settings + + )} @@ -107,6 +114,11 @@ export default function CIPPSettings() { + + + + + ) diff --git a/src/views/cipp/app-settings/SettingsSuperAdmin.jsx b/src/views/cipp/app-settings/SettingsSuperAdmin.jsx new file mode 100644 index 000000000000..41d8332387a3 --- /dev/null +++ b/src/views/cipp/app-settings/SettingsSuperAdmin.jsx @@ -0,0 +1,113 @@ +import { useGenericGetRequestQuery, useLazyGenericPostRequestQuery } from 'src/store/api/app.js' +import { + CButton, + CCallout, + CCard, + CCardBody, + CCardHeader, + CCol, + CForm, + CLink, + CRow, + CSpinner, +} from '@coreui/react' +import { Form } from 'react-final-form' +import { RFFCFormRadio } from 'src/components/forms/index.js' +import React from 'react' +import { CippCallout } from 'src/components/layout/index.js' + +export function SettingsSuperAdmin() { + const partnerConfig = useGenericGetRequestQuery({ + path: '/api/ExecPartnerMode', + params: { Action: 'ListCurrent' }, + }) + + const [submitWebhook, webhookCreateResult] = useLazyGenericPostRequestQuery() + + const onSubmit = (values) => { + submitWebhook({ + path: '/api/ExecPartnerMode', + values: values, + }).then((res) => {}) + } + + return ( + + + + <> + <> +

Super Admin Configuration

+ + +

+ The configuration settings below should only be modified by a super admin. Super + admins can configure what tenant mode CIPP operates in. See + + our documentation + + for more information on how to configure these modes and what they mean. +

+
+
+ + +

Tenant Mode

+
( + <> + {partnerConfig.isFetching && } + + + + + + {webhookCreateResult.isFetching ? ( + <> + + Saving... + + ) : ( + 'Save' + )} + + + + )} + /> + {webhookCreateResult.isSuccess && ( + + {webhookCreateResult?.data?.results} + + )} + + + + + + + ) +} From 1665004feec99e4bfbbc32de3354bc743f7742f2 Mon Sep 17 00:00:00 2001 From: greenchiip Date: Wed, 10 Apr 2024 12:36:38 +0200 Subject: [PATCH 021/475] New Sort function New Sort function --- src/components/utilities/index.js | 2 ++ .../utilities/validateAlphabeticalSort.jsx | 23 +++++++++++++++++++ src/views/endpoint/intune/MEMAddPolicy.jsx | 20 +++++++++++----- .../tenant/standards/ListAppliedStandards.jsx | 7 ++++-- 4 files changed, 44 insertions(+), 8 deletions(-) create mode 100644 src/components/utilities/validateAlphabeticalSort.jsx diff --git a/src/components/utilities/index.js b/src/components/utilities/index.js index 11fc6a9581d5..24fed1e5aecb 100644 --- a/src/components/utilities/index.js +++ b/src/components/utilities/index.js @@ -18,6 +18,7 @@ import PageSizeSwitcher from 'src/components/utilities/PageSizeSwitcher' import Toasts from 'src/components/utilities/Toasts' import UsageLocation from 'src/components/utilities/UsageLocation' import CippTableOffcanvas from './CippTableOffcanvas' +import validateAlphabeticalSort from './validateAlphabeticalSort' export { CippActionsOffcanvas, @@ -43,4 +44,5 @@ export { PageSizeSwitcher, Toasts, UsageLocation, + validateAlphabeticalSort, } diff --git a/src/components/utilities/validateAlphabeticalSort.jsx b/src/components/utilities/validateAlphabeticalSort.jsx new file mode 100644 index 000000000000..ff19bc21023e --- /dev/null +++ b/src/components/utilities/validateAlphabeticalSort.jsx @@ -0,0 +1,23 @@ +export default function validateAlphabeticalSort(data, sortKeys) { + if (!sortKeys || sortKeys.length === 0) return data + try { + if (!data) return data + const newList = data.filter((element) => { + return sortKeys.every((key) => { + return (element) => element[key] != null && element[key] != undefined + }) + }) + return newList.sort((a, b) => { + try { + return sortKeys.reduce((acc, key) => { + if (acc !== 0) return acc + return (a[key] ?? '').toString().localeCompare(b[key] ?? '') + }, 0) + } catch (error) { + return 0 + } + }) + } catch (error) { + return data + } +} diff --git a/src/views/endpoint/intune/MEMAddPolicy.jsx b/src/views/endpoint/intune/MEMAddPolicy.jsx index 161b0134665f..0b1de079d1e0 100644 --- a/src/views/endpoint/intune/MEMAddPolicy.jsx +++ b/src/views/endpoint/intune/MEMAddPolicy.jsx @@ -2,9 +2,15 @@ import React, { useState } from 'react' import { CCol, CRow, 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 { + faCheck, + faExclamationTriangle, + faFunnelDollar, + faTimes, +} from '@fortawesome/free-solid-svg-icons' import { CippWizard } from 'src/components/layout' import { WizardTableField } from 'src/components/tables' +import { validateAlphabeticalSort } from 'src/components/utilities' import PropTypes from 'prop-types' import { Condition, @@ -16,6 +22,7 @@ import { import { useLazyGenericGetRequestQuery, useLazyGenericPostRequestQuery } from 'src/store/api/app' import { OnChange } from 'react-final-form-listeners' import CippJsonView from 'src/components/utilities/CippJsonView' +import { value } from 'lodash-es' const Error = ({ name }) => ( { let template = intuneTemplates.data.filter(function (obj) { return obj.GUID === value }) - // console.log(template[0][set]) onChange(template[0][set]) }} @@ -145,10 +151,12 @@ const AddPolicy = () => { {intuneTemplates.isSuccess && ( ({ - value: template.GUID, - label: template.Displayname, - }))} + values={validateAlphabeticalSort(intuneTemplates.data, ['Displayname'])?.map( + (template) => ({ + value: template.GUID, + label: template.Displayname, + }), + )} placeholder="Select a template" label="Please choose a template to apply." /> diff --git a/src/views/tenant/standards/ListAppliedStandards.jsx b/src/views/tenant/standards/ListAppliedStandards.jsx index 62a679ff1d9a..72852dfd13dd 100644 --- a/src/views/tenant/standards/ListAppliedStandards.jsx +++ b/src/views/tenant/standards/ListAppliedStandards.jsx @@ -31,7 +31,7 @@ import { import { faCheck, faCircleNotch, faExclamationTriangle } from '@fortawesome/free-solid-svg-icons' import { CippContentCard, CippPage } from 'src/components/layout' import { useSelector } from 'react-redux' -import { ModalService } from 'src/components/utilities' +import { ModalService, validateAlphabeticalSort } from 'src/components/utilities' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import Skeleton from 'react-loading-skeleton' import { CippTable, cellBooleanFormatter } from 'src/components/tables' @@ -613,7 +613,10 @@ const ApplyNewStandard = () => { name={`${template.switchName}.TemplateList`} className="mb-3" multi={true} - values={template.templates.data?.map((t) => ({ + values={validateAlphabeticalSort( + template.templates.data, + ['Displayname', 'name'], + )?.map((t) => ({ value: t.GUID, name: t.name || t.Displayname || t.displayName, }))} From 091aa6fcd56acafd7ca3bbd4ec1cdb0afd4a037a Mon Sep 17 00:00:00 2001 From: greenchiip Date: Wed, 17 Apr 2024 13:02:27 +0200 Subject: [PATCH 022/475] added number as type to RFFCFormInput --- src/components/forms/RFFComponents.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/forms/RFFComponents.jsx b/src/components/forms/RFFComponents.jsx index 0d07909c5975..234e990ca5c5 100644 --- a/src/components/forms/RFFComponents.jsx +++ b/src/components/forms/RFFComponents.jsx @@ -193,7 +193,7 @@ export const RFFCFormInput = ({ } RFFCFormInput.propTypes = { ...sharedPropTypes, - type: PropTypes.oneOf(['color', 'file', 'text', 'password']), + type: PropTypes.oneOf(['color', 'file', 'text', 'password', 'number']), placeholder: PropTypes.string, } From 139d1cdfb01cd61ee8472f34385e6b40e370cafe Mon Sep 17 00:00:00 2001 From: greenchiip Date: Wed, 17 Apr 2024 13:05:54 +0200 Subject: [PATCH 023/475] Added missing placeholder tag --- src/views/tenant/standards/ListAppliedStandards.jsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/views/tenant/standards/ListAppliedStandards.jsx b/src/views/tenant/standards/ListAppliedStandards.jsx index 72852dfd13dd..8a280ca6f830 100644 --- a/src/views/tenant/standards/ListAppliedStandards.jsx +++ b/src/views/tenant/standards/ListAppliedStandards.jsx @@ -509,6 +509,7 @@ const ApplyNewStandard = () => { <> {component.type === 'Select' && ( Date: Thu, 18 Apr 2024 23:35:33 +0200 Subject: [PATCH 024/475] Support single tenant Service health report --- src/views/tenant/administration/ServiceHealth.jsx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/views/tenant/administration/ServiceHealth.jsx b/src/views/tenant/administration/ServiceHealth.jsx index 30d3a5e0199c..7da9f0bc195c 100644 --- a/src/views/tenant/administration/ServiceHealth.jsx +++ b/src/views/tenant/administration/ServiceHealth.jsx @@ -1,4 +1,5 @@ import React from 'react' +import { useSelector } from 'react-redux' import { CippPageList } from 'src/components/layout' import { CellTip } from 'src/components/tables' @@ -37,6 +38,7 @@ const columns = [ ] const ServiceHealth = () => { + const currentTenant = useSelector((state) => state.app.currentTenant) return ( { datatable={{ columns, path: '/api/ListServiceHealth', + params: { + tenantFilter: currentTenant.customerId, + displayName: currentTenant.displayName, + defaultDomainName: currentTenant.defaultDomainName, + }, reportName: `Service-Health-Report`, }} /> From f19bb32b665ab63ccb78ae34b30ff13b5ae7ce15 Mon Sep 17 00:00:00 2001 From: Esco Date: Fri, 19 Apr 2024 13:10:35 +0200 Subject: [PATCH 025/475] Added TenantDefaultTimezone --- src/data/standards.json | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/data/standards.json b/src/data/standards.json index cab53f3144d6..07d9cbacf322 100644 --- a/src/data/standards.json +++ b/src/data/standards.json @@ -1173,5 +1173,21 @@ "label": "Only allow users to sync OneDrive from AAD joined devices", "impact": "High Impact", "impactColour": "danger" + }, + { + "name": "standards.TenantDefaultTimezone", + "cat": "SharePoint Standards", + "tag": ["lowimpact"], + "helpText": "Sets the default timezone for the tenant. This will be used for all new users and sites.", + "addedComponent": [ + { + "type": "input", + "name": "standards.TenantDefaultTimezone.Timezone", + "label": "Timezone" + } + ], + "label": "Set Default Timezone for Tenant", + "impact": "Low Impact", + "impactColour": "info" } ] From 55601100ad2ed0a2d514054b076f10d586e32986 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Sun, 21 Apr 2024 16:38:21 +0200 Subject: [PATCH 026/475] added securescore overviews. --- src/data/standards.json | 41 +- src/importsMap.jsx | 1 + src/routes.json | 6 + src/scss/_themes.scss | 2 +- .../tenant/administration/SecureScore.jsx | 416 ++++++++++++++++++ 5 files changed, 452 insertions(+), 14 deletions(-) create mode 100644 src/views/tenant/administration/SecureScore.jsx diff --git a/src/data/standards.json b/src/data/standards.json index cab53f3144d6..ecde5a79d459 100644 --- a/src/data/standards.json +++ b/src/data/standards.json @@ -38,7 +38,7 @@ { "name": "standards.AuditLog", "cat": "Global Standards", - "tag": ["lowimpact", "CIS"], + "tag": ["lowimpact", "CIS", "mip_search_auditlog"], "helpText": "Enables the Unified Audit Log for tracking and auditing activities. Also runs Enable-OrganizationCustomization if necessary.", "addedComponent": [], "label": "Enable the Unified Audit Log", @@ -63,7 +63,7 @@ { "name": "standards.EnableCustomerLockbox", "cat": "Global Standards", - "tag": ["lowimpact", "CIS"], + "tag": ["lowimpact", "CIS", "CustomerLockBoxEnabled"], "helpText": "Enables Customer Lockbox that offers an approval process for Microsoft support to access organization data", "addedComponent": [], "label": "Enable Customer Lockbox", @@ -103,7 +103,7 @@ { "name": "standards.ActivityBasedTimeout", "cat": "Global Standards", - "tag": ["mediumimpact", "CIS"], + "tag": ["mediumimpact", "CIS", "spo_idle_session_timeout"], "helpText": "Enables and sets Idle session timeout for Microsoft 365 to 1 hour. This policy affects most M365 web apps", "addedComponent": [], "label": "Enable 1 hour Activity based Timeout", @@ -225,7 +225,7 @@ { "name": "standards.PasswordExpireDisabled", "cat": "Entra (AAD) Standards", - "tag": ["lowimpact", "CIS"], + "tag": ["lowimpact", "CIS", "PWAgePolicyNew"], "helpText": "Disables the expiration of passwords for the tenant by setting the password expiration policy to never expire for any user.", "addedComponent": [], "label": "Do not expire passwords", @@ -535,7 +535,7 @@ { "name": "standards.EnableMailTips", "cat": "Exchange Standards", - "tag": ["lowimpact", "CIS"], + "tag": ["lowimpact", "CIS", "exo_mailtipsenabled"], "helpText": "Enables all MailTips in Outlook. MailTips are the notifications Outlook and Outlook on the web shows when an email you create, meets some requirements", "addedComponent": [ { @@ -582,7 +582,7 @@ { "name": "standards.EnableMailboxAuditing", "cat": "Exchange Standards", - "tag": ["lowimpact", "CIS"], + "tag": ["lowimpact", "CIS", "exo_mailboxaudit"], "helpText": "Enables Mailbox auditing for all mailboxes and on tenant level. Disables audit bypass on all mailboxes. Unified Audit Log needs to be enabled for this standard to function.", "addedComponent": [], "label": "Enable Mailbox auditing", @@ -664,7 +664,7 @@ { "name": "standards.DisableExternalCalendarSharing", "cat": "Exchange Standards", - "tag": ["lowimpact", "CIS"], + "tag": ["lowimpact", "CIS", "exo_individualsharing"], "helpText": "Disables the ability for users to share their calendar with external users. Only for the default policy, so exclusions can be made if needed.", "addedComponent": [], "label": "Disable external calendar sharing", @@ -674,7 +674,7 @@ { "name": "standards.DisableAdditionalStorageProviders", "cat": "Exchange Standards", - "tag": ["lowimpact", "CIS"], + "tag": ["lowimpact", "CIS", "exo_storageproviderrestricted"], "helpText": "Disables the ability for users to open files in Outlook on the Web, from other providers such as Box, Dropbox, Facebook, Google Drive, OneDrive Personal, etc.", "addedComponent": [], "label": "Disable additional storage providers in OWA", @@ -684,7 +684,7 @@ { "name": "standards.DisableOutlookAddins", "cat": "Exchange Standards", - "tag": ["mediumimpact", "CIS"], + "tag": ["mediumimpact", "CIS", "exo_outlookaddins"], "helpText": "Disables the ability for users to install add-ins in Outlook. This is to prevent users from installing malicious add-ins.", "addedComponent": [], "label": "Disable users from installing add-ins in Outlook", @@ -765,7 +765,7 @@ { "name": "standards.SafeLinksPolicy", "cat": "Defender Standards", - "tag": ["lowimpact", "CIS"], + "tag": ["lowimpact", "CIS", "mdo_safelinksforemail", "mdo_safelinksforOfficeApps"], "helpText": "This creates a safelink policy that automatically scans, tracks, and and enables safe links for Email, Office, and Teams for both external and internal senders", "addedComponent": [ { @@ -791,7 +791,16 @@ { "name": "standards.AntiPhishPolicy", "cat": "Defender Standards", - "tag": ["lowimpact", "CIS"], + "tag": [ + "lowimpact", + "CIS", + "mdo_safeattachments", + "mdo_highconfidencespamaction", + "mdo_highconfidencephishaction", + "mdo_phisspamacation", + "mdo_spam_notifications_only_for_admins", + "mdo_antiphishingpolicies" + ], "helpText": "This creates a Anti-Phishing policy that automatically enables Mailbox Intelligence and spoofing, optional switches for Mailtips.", "addedComponent": [ { @@ -870,7 +879,13 @@ { "name": "standards.SafeAttachmentPolicy", "cat": "Defender Standards", - "tag": ["lowimpact", "CIS"], + "tag": [ + "lowimpact", + "CIS", + "mdo_safedocuments", + "mdo_commonattachmentsfilter", + "mdo_safeattachmentpolicy" + ], "helpText": "This creates a Safe Attachment policy", "addedComponent": [ { @@ -946,7 +961,7 @@ { "name": "standards.MalwareFilterPolicy", "cat": "Defender Standards", - "tag": ["lowimpact", "CIS"], + "tag": ["lowimpact", "CIS", "mdo_zapspam", "mdo_zapphish", "mdo_zapmalware"], "helpText": "This creates a Malware filter policy that enables the default File filter and Zero-hour auto purge for malware.", "addedComponent": [ { diff --git a/src/importsMap.jsx b/src/importsMap.jsx index ab783b321471..0121566555bd 100644 --- a/src/importsMap.jsx +++ b/src/importsMap.jsx @@ -125,6 +125,7 @@ import React from 'react' "/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')), diff --git a/src/routes.json b/src/routes.json index db9ee316185f..3d8eb476d3fb 100644 --- a/src/routes.json +++ b/src/routes.json @@ -864,6 +864,12 @@ "component": "views/cipp/Setup", "allowedRoles": ["admin"] }, + { + "path": "/tenant/administration/securescore", + "name": "Secure Score Management", + "component": "views/tenant/administration/SecureScore", + "allowedRoles": ["admin", "editor", "readonly"] + }, { "path": "/tenant/administration/gdap", "name": "GDAP Wizard", diff --git a/src/scss/_themes.scss b/src/scss/_themes.scss index 3c1be2d0cecf..f05c2be45f9b 100644 --- a/src/scss/_themes.scss +++ b/src/scss/_themes.scss @@ -479,7 +479,7 @@ --cui-toast-color: var(--cui-color-black); --cui-toast-header-color: var(--cui-color-black); --cui-card-cap-color: var(--cyberdrain-white); - --cui-card-cap-bg: var(--cui-color-dark); + --cui-card-cap-bg: var(--cui-color-gray-hover); --cui-tertiary-bg: var(--cui-bgcolor-table-header); // CIPP Impact theme variables. --cipp-toast-bg: var(--cui-color-header-bar); diff --git a/src/views/tenant/administration/SecureScore.jsx b/src/views/tenant/administration/SecureScore.jsx new file mode 100644 index 000000000000..423d6c8611de --- /dev/null +++ b/src/views/tenant/administration/SecureScore.jsx @@ -0,0 +1,416 @@ +import React, { useEffect, useRef } from 'react' +import { + CBadge, + CButton, + CCard, + CCardBody, + CCardFooter, + CCardHeader, + CCardText, + CCardTitle, + CCol, + CFormInput, + CFormSelect, + CFormSwitch, + CRow, +} from '@coreui/react' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { faCheck, faTimes, faExclamation } from '@fortawesome/free-solid-svg-icons' +import { CippTable } from 'src/components/tables' +import { CippPage } from 'src/components/layout/CippPage' +import { useGenericGetRequestQuery, useLazyGenericPostRequestQuery } from 'src/store/api/app' +import { useSelector } from 'react-redux' +import Skeleton from 'react-loading-skeleton' +import standards from 'src/data/standards' +import { useNavigate } from 'react-router-dom' +import { ModalService } from 'src/components/utilities' +import { CellTip, cellGenericFormatter } from 'src/components/tables/CellGenericFormat' + +const SecureScore = () => { + const textRef = useRef() + const selectRef = useRef() + const currentTenant = useSelector((state) => state.app.currentTenant) + const [viewMode, setViewMode] = React.useState(false) + const [translateData, setTranslatedData] = React.useState([]) + const [translateState, setTranslateSuccess] = React.useState(false) + const [genericPostRequest, postResults] = useLazyGenericPostRequestQuery() + const [sortBy, setSortBy] = React.useState() + const { + data: securescore = [], + isFetching, + isSuccess, + } = useGenericGetRequestQuery({ + path: '/api/ListGraphRequest', + params: { + tenantFilter: currentTenant.defaultDomainName, + Endpoint: 'security/secureScores', + $top: 1, + NoPagination: true, + }, + }) + + const { data: securescoreTranslation = [], isSuccess: isSuccessTranslation } = + useGenericGetRequestQuery({ + path: '/api/ListGraphRequest', + params: { + tenantFilter: currentTenant.defaultDomainName, + Endpoint: 'security/secureScoreControlProfiles', + $top: 999, + NoPagination: true, + }, + }) + + useEffect(() => { + if (isSuccess) { + setTranslatedData(securescore.Results[0]) + setTranslateSuccess(true) + } + }, [isSuccess, securescore.Results]) + + useEffect(() => { + if (isSuccess && isSuccessTranslation) { + const updatedControlScores = translateData.controlScores.map((control) => { + const translation = securescoreTranslation.Results?.find( + (controlTranslation) => controlTranslation.id === control.controlName, + ) + const remediation = standards.find((standard) => standard.tag.includes(control.controlName)) + return { + ...control, + title: translation?.title, + threats: translation?.threats, + complianceInformation: translation?.complianceInformation, + actionUrl: remediation + ? '/tenant/standards/list-applied-standards' + : translation?.actionUrl, + remediation: remediation + ? `1. Enable the CIPP Standard: ${remediation.label}` + : translation?.remediation, + remediationImpact: translation?.remediationImpact, + implementationCost: translation?.implementationCost, + tier: translation?.tier, + userImpact: translation?.userImpact, + vendorInformation: translation?.vendorInformation, + controlStateUpdates: translation?.controlStateUpdates[0] + ? translation.controlStateUpdates + : [], + } + }) + + updatedControlScores.sort((a, b) => { + return b['scoreInPercentage'] - a['scoreInPercentage'] + }) + setTranslatedData((prevData) => ({ + ...prevData, + controlScores: updatedControlScores, + })) + } + }, [isSuccess, isSuccessTranslation, securescoreTranslation.Results]) + //create open function, if remediation starts with https, open in new tab. Else, use router to navigate to the remediation page. + const navigate = useNavigate() + + const openRemediation = (url) => { + if (url.startsWith('https')) { + window.open(url, '_blank') + } else { + navigate(url) + } + } + const openResolution = (control) => { + ModalService.confirm({ + key: control, + body: ( +
+
+ +
+
+ +
+
+ ), + title: 'Confirm', + onConfirm: () => + genericPostRequest({ + path: '/api/ExecUpdateSecureScore', + values: { + controlName: control.controlName, + resolutionType: selectRef.current.value, + reason: textRef.current.value, + tenantFilter: currentTenant.defaultDomainName, + vendorinformation: control.vendorInformation, + }, + }), + }) + } + + const columns = [ + { + name: 'Task Title', + selector: (row) => row['title'], + sortable: true, + cell: (row) => CellTip(row['title']), + exportSelector: 'title', + }, + { + name: 'Percentage Complete', + selector: (row) => row['scoreInPercentage'], + sortable: true, + cell: (row) => CellTip(row['scoreInPercentage']), + exportSelector: 'scoreInPercentage', + }, + { + name: 'Remediation', + selector: (row) => row['actionUrl'], + sortable: true, + cell: cellGenericFormatter(), + exportSelector: 'actionUrl', + }, + ] + + return ( + <> + + + + + Overview mode + + + + setViewMode(!viewMode)} /> + + + + + + + + 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 + + + )} +
+
+
+
+
+ + {viewMode && translateData.controlScores.length > 1 && ( + + + Best Practice Report + + + + + + )} + {translateState && !viewMode && ( + <> + + {translateData?.controlScores?.map((info, idx) => ( + + + + {info.title} + {console.log(info)} + + + + + + {info.scoreInPercentage === 100 + ? `100% ${ + info.controlStateUpdates?.length > 0 && + info.controlStateUpdates[0].state !== 'Default' + ? `(${info?.controlStateUpdates[0]?.state})` + : '' + }` + : `${info.scoreInPercentage}% ${ + info.controlStateUpdates?.length > 0 && + info.controlStateUpdates[0].state !== 'Default' + ? `(${info?.controlStateUpdates[0]?.state})` + : '' + } + `} + + + + +
Description
+ +
+ + + {info.scoreInPercentage !== 100 && ( + +
Remediation Recommendation
+ + { +
+ } + + + )} + + {info.threats?.length > 0 && ( + <> +
Threats
+ {info.threats?.map((threat, idx) => ( + + {threat} + + ))} + + )} +
+ + {info.complianceInformation > 0 && ( + <> +
Compliance Frameworks
+ {info.complianceInformation?.map((framework, idx) => ( + + {framework.certificationName} -{' '} + {framework.certificationControls[0]?.name} + + ))} + + )} +
+ + + openRemediation(info.actionUrl)} + className="me-3" + > + Remediate + + openResolution(info)} className="me-3"> + Change Status + + + + + ))} + + + )} + + + ) +} + +export default SecureScore From aec93f1a9617d9c46dd2a0582840f8b054d4f8ec Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Sun, 21 Apr 2024 17:24:33 +0200 Subject: [PATCH 027/475] remove comments, fixes reload --- .../tenant/administration/SecureScore.jsx | 50 +++++++++++++------ 1 file changed, 35 insertions(+), 15 deletions(-) diff --git a/src/views/tenant/administration/SecureScore.jsx b/src/views/tenant/administration/SecureScore.jsx index 423d6c8611de..81ac84af7878 100644 --- a/src/views/tenant/administration/SecureScore.jsx +++ b/src/views/tenant/administration/SecureScore.jsx @@ -25,6 +25,7 @@ import standards from 'src/data/standards' 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' const SecureScore = () => { const textRef = useRef() @@ -34,13 +35,13 @@ const SecureScore = () => { const [translateData, setTranslatedData] = React.useState([]) const [translateState, setTranslateSuccess] = React.useState(false) const [genericPostRequest, postResults] = useLazyGenericPostRequestQuery() - const [sortBy, setSortBy] = React.useState() + const [refreshCode, setRefresh] = React.useState(null) const { data: securescore = [], isFetching, isSuccess, } = useGenericGetRequestQuery({ - path: '/api/ListGraphRequest', + path: '/api/ListGraphRequest?refresh=' + refreshCode, params: { tenantFilter: currentTenant.defaultDomainName, Endpoint: 'security/secureScores', @@ -49,16 +50,19 @@ const SecureScore = () => { }, }) - const { data: securescoreTranslation = [], isSuccess: isSuccessTranslation } = - useGenericGetRequestQuery({ - path: '/api/ListGraphRequest', - params: { - tenantFilter: currentTenant.defaultDomainName, - Endpoint: 'security/secureScoreControlProfiles', - $top: 999, - NoPagination: true, - }, - }) + const { + data: securescoreTranslation = [], + isSuccess: isSuccessTranslation, + isFetching: isFetchingTranslation, + } = useGenericGetRequestQuery({ + path: '/api/ListGraphRequest?refresh=' + refreshCode, + params: { + tenantFilter: currentTenant.defaultDomainName, + Endpoint: 'security/secureScoreControlProfiles', + $top: 999, + NoPagination: true, + }, + }) useEffect(() => { if (isSuccess) { @@ -104,8 +108,7 @@ const SecureScore = () => { controlScores: updatedControlScores, })) } - }, [isSuccess, isSuccessTranslation, securescoreTranslation.Results]) - //create open function, if remediation starts with https, open in new tab. Else, use router to navigate to the remediation page. + }, [isSuccess, isSuccessTranslation, securescoreTranslation.Results, refreshCode]) const navigate = useNavigate() const openRemediation = (url) => { @@ -139,6 +142,10 @@ const SecureScore = () => { value: 'Ignored', label: 'Ignored / Risk Accepted (Mark as completed, do not receive points)', }, + { + value: 'Default', + label: 'Mark as default (Receive points if Microsoft detects as completed)', + }, ]} label="Resolution Type" /> @@ -156,6 +163,8 @@ const SecureScore = () => { tenantFilter: currentTenant.defaultDomainName, vendorinformation: control.vendorInformation, }, + }).then(() => { + setRefresh(Math.random()) }), }) } @@ -186,6 +195,17 @@ const SecureScore = () => { return ( <> + {postResults.isFetching && } + {postResults.isSuccess && ( + + {postResults.data.Results} + + )} + {postResults.isError && ( + + {postResults.error.message} + + )} @@ -306,7 +326,7 @@ const SecureScore = () => { )} - {translateState && !viewMode && ( + {translateState && !viewMode && !isFetching && ( <> {translateData?.controlScores?.map((info, idx) => ( From e61e475092b38c012adf26e39c8e990cbd3f9ecf Mon Sep 17 00:00:00 2001 From: John Duprey Date: Mon, 22 Apr 2024 12:24:54 -0400 Subject: [PATCH 028/475] CippActionsOffcanvas updates - Add dismissable callout to code block - Add progress bar to cards - Update queue to include progress percentage --- src/components/layout/AppHeader.jsx | 5 +++- .../utilities/CippActionsOffcanvas.jsx | 25 ++++++++++++++++++- src/components/utilities/CippCodeBlock.jsx | 11 +++++++- src/components/utilities/CippOffcanvas.jsx | 6 ++--- 4 files changed, 41 insertions(+), 6 deletions(-) diff --git a/src/components/layout/AppHeader.jsx b/src/components/layout/AppHeader.jsx index 43ec9c476d35..85fe747dd98f 100644 --- a/src/components/layout/AppHeader.jsx +++ b/src/components/layout/AppHeader.jsx @@ -94,11 +94,13 @@ const AppHeader = () => { value: job.Status, link: job.Link, timestamp: job.Timestamp, + percent: job.PercentComplete, + progressText: `${job.PercentComplete}%`, })), ) } else { setCippQueueExtendedInfo([ - { label: 'No jobs to display', value: '', timpestamp: Date(), link: '#' }, + { label: 'No jobs to display', value: '', timestamp: Date(), link: '#' }, ]) } }, [cippQueueList]) @@ -197,6 +199,7 @@ const AppHeader = () => { extendedInfo={[]} cards={cippQueueExtendedInfo} refreshFunction={refreshCippQueue} + isRefreshing={cippQueueList.isFetching || cippQueueList.isLoading} actions={[ { label: 'Clear History', diff --git a/src/components/utilities/CippActionsOffcanvas.jsx b/src/components/utilities/CippActionsOffcanvas.jsx index c2cab6d24539..dc9567479ab5 100644 --- a/src/components/utilities/CippActionsOffcanvas.jsx +++ b/src/components/utilities/CippActionsOffcanvas.jsx @@ -5,14 +5,20 @@ import { CCallout, CCard, CCardBody, + CCardFooter, CCardHeader, CCardText, CCardTitle, + CCol, CFormInput, CFormSelect, CListGroup, CListGroupItem, COffcanvasTitle, + CProgress, + CProgressBar, + CProgressStacked, + CRow, CSpinner, } from '@coreui/react' import { CippCodeBlock, CippOffcanvas, ModalService } from 'src/components/utilities' @@ -222,8 +228,23 @@ export default function CippActionsOffcanvas(props) { {action.value && Status: {action.value}} - {action.timestamp && } + + + {action.percent && ( + +
+ + {action?.progressText} + +
+
+ )} + + {action.timestamp && } + +
+
)) @@ -295,6 +316,7 @@ export default function CippActionsOffcanvas(props) { id={props.id} hideFunction={props.hideFunction} refreshFunction={props.refreshFunction} + isRefreshing={props.isRefreshing} > {getResults.isFetching && ( @@ -310,6 +332,7 @@ export default function CippActionsOffcanvas(props) { )} diff --git a/src/components/utilities/CippCodeBlock.jsx b/src/components/utilities/CippCodeBlock.jsx index aad81e02d78d..00ccd3c54f7c 100644 --- a/src/components/utilities/CippCodeBlock.jsx +++ b/src/components/utilities/CippCodeBlock.jsx @@ -16,6 +16,7 @@ function CippCodeBlock({ callout = false, calloutColour = 'info', calloutCopyValue = false, + dismissable = false, }) { const [codeCopied, setCodeCopied] = useState(false) @@ -36,7 +37,11 @@ function CippCodeBlock({ {codeCopied ? : } - {callout && {code}} + {callout && ( + + {code} + + )} {!callout && ( { - //console.log('refresh') props.refreshFunction() }} > - + {props.isRefreshing ? : } )} @@ -48,6 +47,7 @@ export const CippOffcanvasPropTypes = { id: PropTypes.string, hideFunction: PropTypes.func.isRequired, refreshFunction: PropTypes.func, + isRefreshing: PropTypes.bool, addedClass: PropTypes.string, } From 6f5ab1cf767471830ea4110253ec3d9cc06c9252 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Mon, 22 Apr 2024 22:26:35 +0200 Subject: [PATCH 029/475] settings menu interface changes --- .../contentcards/CippButtonCard.jsx | 27 ++ .../cipp/app-settings/SettingsGeneral.jsx | 406 +++++++++--------- .../components/SettingsDNSResolver.jsx | 67 +-- .../components/SettingsGeneralRow.jsx | 261 ++++++----- .../components/SettingsPassword.jsx | 73 ++-- 5 files changed, 473 insertions(+), 361 deletions(-) create mode 100644 src/components/contentcards/CippButtonCard.jsx diff --git a/src/components/contentcards/CippButtonCard.jsx b/src/components/contentcards/CippButtonCard.jsx new file mode 100644 index 000000000000..1540c09a888f --- /dev/null +++ b/src/components/contentcards/CippButtonCard.jsx @@ -0,0 +1,27 @@ +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' + +export default function CippButtonCard({ + title, + titleType = 'normal', + CardButton, + children, + isFetching, +}) { + return ( + + + + {titleType === 'big' ?

{title}

: title} +
+
+ + {isFetching && } + {children} + + {CardButton} +
+ ) +} diff --git a/src/views/cipp/app-settings/SettingsGeneral.jsx b/src/views/cipp/app-settings/SettingsGeneral.jsx index db6494e17402..98a34210c72b 100644 --- a/src/views/cipp/app-settings/SettingsGeneral.jsx +++ b/src/views/cipp/app-settings/SettingsGeneral.jsx @@ -28,6 +28,7 @@ import { TableModalButton } from 'src/components/buttons/index.js' 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' /** * SettingsGeneral component. @@ -217,7 +218,43 @@ export function SettingsGeneral() { />, ], } + const permissionsCheckButton = ( + checkPermissions()} + disabled={permissionsResult.isFetching} + className="me-2" + > + {permissionsResult.isFetching && ( + + )} + Run Permissions Check + + ) + + const gdapButton = ( + checkGDAP({ path: '/api/ExecAccessChecks?GDAP=true' })} + disabled={GDAPResult.isFetching} + className="me-2" + > + {GDAPResult.isFetching && ( + + )} + Run GDAP Check + + ) + const tenantAccessCheckButton = ( + handleCheckAccess()} + disabled={accessCheckResult.isFetching || selectedTenants.length < 1} + > + {accessCheckResult.isFetching && ( + + )} + Run access check + + ) return (
@@ -227,218 +264,193 @@ export function SettingsGeneral() { - - - -

Permissions Check

-

Click the button below to start a permissions check.

- checkPermissions()} - disabled={permissionsResult.isFetching} - className="mb-3 me-2" - > - {permissionsResult.isFetching && ( - + +

Click the button below to start a permissions check.

+ + {permissionsResult.isSuccess && ( + <> + {permissionsResult.data.Results?.AccessTokenDetails?.Name !== '' && ( + <> + setTokenOffcanvasVisible(true)}> + Details + + setTokenOffcanvasVisible(false)} + /> + )} - Run Permissions Check -
- {permissionsResult.isSuccess && ( - <> - {permissionsResult.data.Results?.AccessTokenDetails?.Name !== '' && ( + + {permissionsResult.data.Results?.Messages && ( <> - setTokenOffcanvasVisible(true)}> - Details - - setTokenOffcanvasVisible(false)} - /> + {permissionsResult.data.Results?.Messages?.map((m, idx) => ( +
{m}
+ ))} )} - - {permissionsResult.data.Results?.Messages && ( - <> - {permissionsResult.data.Results?.Messages?.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} - ))} - - - )} -
- - )} -
-
+ + + )} + + + )} +
- - - -

GDAP Check

-

Click the button below to start a check for general GDAP settings.

- checkGDAP({ path: '/api/ExecAccessChecks?GDAP=true' })} - disabled={GDAPResult.isFetching} - className="mb-3 me-2" - > - {GDAPResult.isFetching && ( - - )} - Run GDAP Check - - {GDAPResult.isSuccess && ( - <> - p['@odata.type'] == '#microsoft.graph.group', - )} - title="Groups" - /> - p['@odata.type'] == '#microsoft.graph.directoryRole', - )} - title="Roles" - /> - - )} - - - {GDAPResult.isSuccess && GDAPResult.data.Results.GDAPIssues?.length > 0 && ( - <> - {GDAPResult.data.Results.GDAPIssues?.filter((e) => e.Type === 'Error') - .length > 0 && ( - - Relationship errors detected. Review the table below for more details. - - )} - {GDAPResult.data.Results.GDAPIssues?.filter((e) => e.Type === 'Warning') - .length > 0 && ( - - Relationship warnings detected. Review the table below for more details. - - )} - - + +

Click the button below to start a check for general GDAP settings.

+ + {GDAPResult.isSuccess && ( + <> + p['@odata.type'] == '#microsoft.graph.group', )} - {GDAPResult.isSuccess && GDAPResult.data.Results.GDAPIssues?.length === 0 && ( - - No relationships with issues found. Please perform a Permissions Check or - Tenant Access Check if you are experiencing issues. - + title="Groups" + /> + p['@odata.type'] == '#microsoft.graph.directoryRole', )} -
-
-
-
+ title="Roles" + /> + + )} + + + {GDAPResult.isSuccess && GDAPResult.data.Results.GDAPIssues?.length > 0 && ( + <> + {GDAPResult.data.Results.GDAPIssues?.filter((e) => e.Type === 'Error').length > + 0 && ( + + Relationship errors detected. Review the table below for more details. + + )} + {GDAPResult.data.Results.GDAPIssues?.filter((e) => e.Type === 'Warning') + .length > 0 && ( + + Relationship warnings detected. Review the table below for more details. + + )} + + + )} + {GDAPResult.isSuccess && GDAPResult.data.Results.GDAPIssues?.length === 0 && ( + + No relationships with issues found. Please perform a Permissions Check or Tenant + Access Check if you are experiencing issues. + + )} + + +
- - - -

Tenant Access Check

- - -
- Click the button below to start a tenant access check. You can select multiple, - but a maximum of {maxSelected + 1} tenants is recommended. -
+ + + +
+ Click the button below to start a tenant access check. You can select multiple, + but a maximum of {maxSelected + 1} tenants is recommended. +
- - handleSetSelectedTenants( - value.map((val) => { - return val.value - }), - ) - } - /> - {showMaxSelected && ( - - A maximum of {maxSelected + 1} tenants is recommended. - - )} -
-
+ + handleSetSelectedTenants( + value.map((val) => { + return val.value + }), + ) + } + /> + {showMaxSelected && ( + + A maximum of {maxSelected + 1} tenants is recommended. + + )} +
+
- - - handleCheckAccess()} - disabled={accessCheckResult.isFetching || selectedTenants.length < 1} - > - {accessCheckResult.isFetching && ( - - )} - Run access check - - - - - - {accessCheckResult.isSuccess && ( - - )} - - -
-
+ + + + + + {accessCheckResult.isSuccess && ( + + )} + + +
diff --git a/src/views/cipp/app-settings/components/SettingsDNSResolver.jsx b/src/views/cipp/app-settings/components/SettingsDNSResolver.jsx index a656578a185a..02067c5c8db2 100644 --- a/src/views/cipp/app-settings/components/SettingsDNSResolver.jsx +++ b/src/views/cipp/app-settings/components/SettingsDNSResolver.jsx @@ -2,6 +2,7 @@ import { CAlert, CButton, CButtonGroup } from '@coreui/react' import React, { useState } from 'react' import { useLazyEditDnsConfigQuery, useLazyGetDnsConfigQuery } from 'src/store/api/domains.js' import { CippCallout } from 'src/components/layout/index.js' +import CippButtonCard from 'src/components/contentcards/CippButtonCard' /** * Sets the DNS resolver based on user selection. @@ -17,34 +18,48 @@ export function SettingsDNSResolver() { await editDnsConfig({ resolver }) await getDnsConfig() } - + const cardbuttonGroup = ( + + {resolvers.map((resolver, index) => ( + switchResolver(resolver)} + color={resolver === getDnsConfigResult.data?.Resolver ? 'primary' : 'secondary'} + key={index} + > + {resolver} + + ))} + + ) return ( <> - {getDnsConfigResult.isUninitialized && getDnsConfig()} - {getDnsConfigResult.isSuccess && ( - <> -

DNS Resolver

- - {resolvers.map((resolver, index) => ( - switchResolver(resolver)} - color={resolver === getDnsConfigResult.data.Resolver ? 'primary' : 'secondary'} - key={index} - > - {resolver} - - ))} - - {(editDnsConfigResult.isSuccess || editDnsConfigResult.isError) && - !editDnsConfigResult.isFetching && ( - - {editDnsConfigResult.isSuccess - ? editDnsConfigResult.data.Results - : 'Error setting resolver'} - - )} - - )} + + {getDnsConfigResult.isUninitialized && getDnsConfig()} + {getDnsConfigResult.isSuccess && ( + <> + + Select your DNS Resolver. The DNS resolve is used for the domain analyser only, and + not for generic DNS resolution. + + {(editDnsConfigResult.isSuccess || editDnsConfigResult.isError) && + !editDnsConfigResult.isFetching && ( + + {editDnsConfigResult.isSuccess + ? editDnsConfigResult.data.Results + : 'Error setting resolver'} + + )} + + )} + ) } diff --git a/src/views/cipp/app-settings/components/SettingsGeneralRow.jsx b/src/views/cipp/app-settings/components/SettingsGeneralRow.jsx index 5e1f01f91e39..1e98f9ecfc8f 100644 --- a/src/views/cipp/app-settings/components/SettingsGeneralRow.jsx +++ b/src/views/cipp/app-settings/components/SettingsGeneralRow.jsx @@ -14,6 +14,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { faCircleNotch } from '@fortawesome/free-solid-svg-icons' import { SettingsPassword } from 'src/views/cipp/app-settings/components/SettingsPassword.jsx' import { SettingsDNSResolver } from 'src/views/cipp/app-settings/components/SettingsDNSResolver.jsx' +import CippButtonCard from 'src/components/contentcards/CippButtonCard' /** * Fetches and maintains DNS configuration settings for the application. @@ -26,7 +27,11 @@ export function SettingsGeneralRow() { const inputRef = useRef(null) const [clearCache, clearCacheResult] = useLazyExecClearCacheQuery() - const { data: versions, isSuccess: isSuccessVersion } = useLoadVersionsQuery() + const { + data: versions, + isSuccess: isSuccessVersion, + refetch: RefechVersion, + } = useLoadVersionsQuery() const downloadTxtFile = (data) => { const txtdata = [JSON.stringify(RunBackupResult.data.backup)] @@ -59,120 +64,158 @@ export function SettingsGeneralRow() { clearCache({ tenantsOnly: true }) }, }) + const refreshVersionButton = ( + RefechVersion()}>Check version update + ) + const cacheButton = ( + <> + handleClearCache()} + disabled={clearCacheResult.isFetching} + > + {clearCacheResult.isFetching && ( + + )} + Clear All Cache + + handleClearCacheTenant()} + disabled={clearCacheResult.isFetching} + > + {clearCacheResult.isFetching && ( + + )} + Clear Tenant Cache + + + ) + const backupButton = ( + <> + runBackup({ path: '/api/ExecRunBackup' })} + disabled={RunBackupResult.isFetching} + > + {RunBackupResult.isFetching && ( + + )} + Run backup + + inputRef.current.click()} + disabled={restoreBackupResult.isFetching} + > + {restoreBackupResult.isFetching && ( + + )} + Restore backup + + + ) return ( <> - - - - - - - - - - - -

Frontend Version

- + + + + + + + + + + +
Latest: {isSuccessVersion ? versions.RemoteCIPPVersion : }
-
- Current: {isSuccessVersion ? versions.LocalCIPPVersion : } -
-
- -

Clear Caches

- handleClearCache()} - disabled={clearCacheResult.isFetching} - > - {clearCacheResult.isFetching && ( - - )} - Clear All Cache - - handleClearCacheTenant()} - disabled={clearCacheResult.isFetching} - > - {clearCacheResult.isFetching && ( - - )} - Clear Tenant Cache - - {clearCacheResult.isSuccess && !clearCacheResult.isFetching && ( - - {clearCacheResult.data?.Results} - - )} -
- -

Settings Backup

- runBackup({ path: '/api/ExecRunBackup' })} - disabled={RunBackupResult.isFetching} +
Current: {isSuccessVersion ? versions.LocalCIPPVersion : }
+
+ +
+
+ + + + + Use this button to clear the caches used by CIPP. This will slow down some aspects of + the application, and should only be used when instructed to do so by support. + + {clearCacheResult.isSuccess && !clearCacheResult.isFetching && ( + - {RunBackupResult.isFetching && ( - - )} - Run backup -
- handleChange(e)} - /> - inputRef.current.click()} - disabled={restoreBackupResult.isFetching} - > - {restoreBackupResult.isFetching && ( - - )} - Restore backup - - {restoreBackupResult.isSuccess && !restoreBackupResult.isFetching && ( - - {restoreBackupResult.data.Results} - - )} - {RunBackupResult.isSuccess && !restoreBackupResult.isFetching && ( - - downloadTxtFile(RunBackupResult.data.backup)}> - Download Backup - - - )} -
- -

Backend API Version

- + {clearCacheResult.data?.Results} + + )} + +
+ + + handleChange(e)} + /> + + Use this button to backup the system configuration for CIPP. This will not include + authentication information or extension configuration. + + + {restoreBackupResult.isSuccess && !restoreBackupResult.isFetching && ( + + {restoreBackupResult.data.Results} + + )} + {RunBackupResult.isSuccess && !restoreBackupResult.isFetching && ( + + downloadTxtFile(RunBackupResult.data.backup)}> + Download Backup + + + )} + + + + + +
Latest: {isSuccessVersion ? versions.RemoteCIPPAPIVersion : }
-
- Current: {isSuccessVersion ? versions.LocalCIPPAPIVersion : } -
-
-
- - +
Current: {isSuccessVersion ? versions.LocalCIPPAPIVersion : }
+
+ + + ) } diff --git a/src/views/cipp/app-settings/components/SettingsPassword.jsx b/src/views/cipp/app-settings/components/SettingsPassword.jsx index aa8ed046d2b5..971f9b161a4b 100644 --- a/src/views/cipp/app-settings/components/SettingsPassword.jsx +++ b/src/views/cipp/app-settings/components/SettingsPassword.jsx @@ -1,7 +1,8 @@ import { useLazyGenericGetRequestQuery, useLazyGenericPostRequestQuery } from 'src/store/api/app.js' import React, { useState } from 'react' -import { CButton, CButtonGroup, CCallout } from '@coreui/react' +import { CButton, CButtonGroup, CCallout, CCardText } from '@coreui/react' import { CippCallout } from 'src/components/layout/index.js' +import CippButtonCard from 'src/components/contentcards/CippButtonCard' /** * This method is responsible for handling password settings in the application. @@ -33,36 +34,50 @@ export function SettingsPassword() { } const resolvers = ['Classic', 'Correct-Battery-Horse'] - + const cardbuttonGroup = ( + + {resolvers.map((r, index) => ( + switchResolver(r)} + color={ + r === getPasswordConfigResult.data?.Results?.passwordType ? 'primary' : 'secondary' + } + key={index} + > + {r} + + ))} + + ) return ( <> - {getPasswordConfigResult.isUninitialized && - getPasswordConfig({ path: '/api/ExecPasswordConfig?list=true' })} -

Password Style

- - {resolvers.map((r, index) => ( - switchResolver(r)} - color={ - r === getPasswordConfigResult.data?.Results?.passwordType ? 'primary' : 'secondary' - } - key={index} - > - {r} - - ))} - - {(editPasswordConfigResult.isSuccess || editPasswordConfigResult.isError) && - !editPasswordConfigResult.isFetching && ( - - {editPasswordConfigResult.isSuccess - ? editPasswordConfigResult.data.Results - : 'Error setting password style'} - - )} + + {getPasswordConfigResult.isUninitialized && + getPasswordConfig({ path: '/api/ExecPasswordConfig?list=true' })} + + + Choose your password style. Classic passwords are a combination of letters and symbols. + Correct-Battery-Horse style is a passphrase, which is easier to remember and more secure + than classic passwords. + + + {(editPasswordConfigResult.isSuccess || editPasswordConfigResult.isError) && + !editPasswordConfigResult.isFetching && ( + + {editPasswordConfigResult.isSuccess + ? editPasswordConfigResult.data.Results + : 'Error setting password style'} + + )} + ) } From 38565da22d337aeb57c7c7a7fe59de325b0e5372 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Mon, 22 Apr 2024 22:54:23 +0200 Subject: [PATCH 030/475] prettification --- .../cipp/app-settings/SettingsBackend.jsx | 383 ++++++++---------- .../cipp/app-settings/SettingsSuperAdmin.jsx | 164 ++++---- 2 files changed, 239 insertions(+), 308 deletions(-) diff --git a/src/views/cipp/app-settings/SettingsBackend.jsx b/src/views/cipp/app-settings/SettingsBackend.jsx index 58e3bc2b594b..c57fa366997c 100644 --- a/src/views/cipp/app-settings/SettingsBackend.jsx +++ b/src/views/cipp/app-settings/SettingsBackend.jsx @@ -11,241 +11,180 @@ import { CRow, } from '@coreui/react' import { CippCodeBlock, CippOffcanvas } from 'src/components/utilities/index.js' +import CippButtonCard from 'src/components/contentcards/CippButtonCard' /** * The SettingsBackend method is responsible for rendering a settings panel that contains several resource * groups and corresponding links to access them. * The panel displays information about Resource Group, Key Vault, Static Web App (Role Management), * Function App (Deployment Center), Function App (Configuration), Function App (Overview), and Cloud Shell. - * + * Wow Kevin, you went hard, sorry I'm going to run it again. // Kelvin 22-04-2024. * @returns {JSX.Element} The settings panel component. */ + +const BackendCardList = [ + { + title: 'Resource Group', + description: + 'The Resource group contains all the CIPP resources in your tenant, except the SAM Application', + link: 'ResourceGroup', + }, + { + title: 'Key Vault', + description: + 'The keyvault allows you to check token information. By default you do not have access.', + link: 'KeyVault', + }, + { + title: 'Static Web App (Role Management)', + description: + 'The Static Web App role management allows you to invite other users to the application.', + link: 'SWARoles', + }, + { + title: 'Function App (Deployment Center)', + description: 'The Function App Deployment Center allows you to run updates on the API', + link: 'FunctionDeployment', + }, + { + title: 'Function App (Configuration)', + description: + 'At the Function App Configuration you can check the status of the API access to your keyvault', + link: 'FunctionConfig', + }, + { + title: 'Function App (Overview)', + description: 'At the function App Overview, you can stop and start the backend API', + link: 'FunctionApp', + }, +] + export function SettingsBackend() { const [listBackend, listBackendResult] = useLazyGenericGetRequestQuery() const [visible, setVisible] = useState(false) + const generateButton = (title, link) => ( + window.open(`${listBackendResult.data?.Results?.[link]}`, '_blank')} + rel="noreferrer" + > + {title} + + ) + return ( -
+ <> {listBackendResult.isUninitialized && listBackend({ path: 'api/ExecBackendURLs' })} - <> - - - - - Resource Group - - -

- The Resource group contains all the CIPP resources in your tenant, except the SAM - Application -

- - Go to Resource Group - -
-
-
- - - - Key Vault - - -

- The keyvault allows you to check token information. By default you do not have - access. -

- - Go to Keyvault - -
-
-
- - - - Static Web App (Role Management) - - -

- The Static Web App role management allows you to invite other users to the - application. -

- - Go to Role Management - -
-
-
-
- - - - - Function App (Deployment Center) - - -

The Function App Deployment Center allows you to run updates on the API

- - Go to Function App Deployment Center - -
-
-
- - - - Function App (Configuration) - - -

- At the Function App Configuration you can check the status of the API access to - your keyvault -

- - Go to Function App Configuration - -
-
-
- - - - Function App (Overview) - - -

At the function App Overview, you can stop and start the backend API

- - Go to Function App Overview - -
-
-
-
- - - - - Cloud Shell - - -

Launch an Azure Cloud Shell Window

- - window.open( - 'https://shell.azure.com/powershell', - '_blank', - 'toolbar=no,scrollbars=yes,resizable=yes,menubar=no,location=no,status=no', - ) - } - rel="noreferrer" - > - Cloud Shell - - setVisible(true)} className="mb-3"> - Command Reference - -
-
+ + {BackendCardList.map((card, index) => ( + + + {card.description} + - - setVisible(false)} - title="Command Reference" - > -
Function App Config
- -
Function App Deployment
- -
Watch Function Logs
- -
Static Web App Config
- -
List CIPP Users
- -
- -
+ ))} + + + + Cloud Shell + + +

Launch an Azure Cloud Shell Window

+ + window.open( + 'https://shell.azure.com/powershell', + '_blank', + 'toolbar=no,scrollbars=yes,resizable=yes,menubar=no,location=no,status=no', + ) + } + rel="noreferrer" + > + Cloud Shell + + setVisible(true)} className="mb-3"> + Command Reference + +
+
+
+ + setVisible(false)} + title="Command Reference" + > +
Function App Config
+ +
Function App Deployment
+ +
Watch Function Logs
+ +
Static Web App Config
+ +
List CIPP Users
+ +
+ ) } diff --git a/src/views/cipp/app-settings/SettingsSuperAdmin.jsx b/src/views/cipp/app-settings/SettingsSuperAdmin.jsx index 41d8332387a3..7650089b5a23 100644 --- a/src/views/cipp/app-settings/SettingsSuperAdmin.jsx +++ b/src/views/cipp/app-settings/SettingsSuperAdmin.jsx @@ -1,20 +1,10 @@ import { useGenericGetRequestQuery, useLazyGenericPostRequestQuery } from 'src/store/api/app.js' -import { - CButton, - CCallout, - CCard, - CCardBody, - CCardHeader, - CCol, - CForm, - CLink, - CRow, - CSpinner, -} from '@coreui/react' +import { CButton, CCol, CForm, CLink, CRow, CSpinner } from '@coreui/react' import { Form } from 'react-final-form' import { RFFCFormRadio } from 'src/components/forms/index.js' import React from 'react' import { CippCallout } from 'src/components/layout/index.js' +import CippButtonCard from 'src/components/contentcards/CippButtonCard' export function SettingsSuperAdmin() { const partnerConfig = useGenericGetRequestQuery({ @@ -30,84 +20,86 @@ export function SettingsSuperAdmin() { values: values, }).then((res) => {}) } + const buttonCard = ( + + {webhookCreateResult.isFetching ? ( + <> + + + ) : ( + 'Save' + )} + + ) return ( - - - + + <> <> - <> -

Super Admin Configuration

- - -

- The configuration settings below should only be modified by a super admin. Super - admins can configure what tenant mode CIPP operates in. See - - our documentation - - for more information on how to configure these modes and what they mean. -

-
-
- - -

Tenant Mode

- ( - <> - {partnerConfig.isFetching && } - - - - - - {webhookCreateResult.isFetching ? ( - <> - - Saving... - - ) : ( - 'Save' - )} - - - - )} - /> - {webhookCreateResult.isSuccess && ( - - {webhookCreateResult?.data?.results} - + + +

+ The configuration settings below should only be modified by a super admin. Super + admins can configure what tenant mode CIPP operates in. See + + our documentation + + for more information on how to configure these modes and what they mean. +

+
+
+ + +

Tenant Mode

+ ( + <> + {partnerConfig.isFetching && } + + + + + + )} -
-
- + /> + {webhookCreateResult.isSuccess && ( + + {webhookCreateResult?.data?.results} + + )} +
+
-
-
+ + ) } From 6897990f0b23aea324d0d0fd70630de5cc2001c8 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Mon, 22 Apr 2024 23:08:50 +0200 Subject: [PATCH 031/475] prettification --- .../app-settings/SettingsNotifications.jsx | 290 +++++++++--------- 1 file changed, 148 insertions(+), 142 deletions(-) diff --git a/src/views/cipp/app-settings/SettingsNotifications.jsx b/src/views/cipp/app-settings/SettingsNotifications.jsx index 6a0b1b73450a..cf66765d185e 100644 --- a/src/views/cipp/app-settings/SettingsNotifications.jsx +++ b/src/views/cipp/app-settings/SettingsNotifications.jsx @@ -19,6 +19,7 @@ import { Form, useForm } from 'react-final-form' import { RFFCFormInput, RFFCFormSwitch, RFFSelectSearch } from 'src/components/forms/index.js' import React from 'react' import { CippCallout } from 'src/components/layout/index.js' +import CippButtonCard from 'src/components/contentcards/CippButtonCard' /** * Sets the notification settings. @@ -33,150 +34,155 @@ export function SettingsNotifications() { } return ( <> - {notificationListResult.isUninitialized && listNotification()} - {notificationListResult.isFetching && ( - - )} - {!notificationListResult.isFetching && notificationListResult.error && ( - Error loading data - )} - {notificationListResult.isSuccess && ( - - - Notifications - - - true} - initialValues={{ - ...notificationListResult.data, - logsToInclude: notificationListResult.data?.logsToInclude?.map((m) => ({ - label: m, - value: m, - })), - Severity: notificationListResult.data?.Severity?.map((s) => ({ - label: s, - value: s, - })), - }} - onSubmit={onSubmit} - render={({ handleSubmit, submitting, values }) => { - return ( - - {notificationConfigResult.isFetching && ( - - Loading - - )} - {notificationConfigResult.isSuccess && !notificationConfigResult.isFetching && ( - - {notificationConfigResult.data?.Results} - - )} - {notificationConfigResult.isError && !notificationConfigResult.isFetching && ( - - Could not connect to API: {notificationConfigResult.error.message} - - )} + + Set Notification Settings + + } + isFetching={notificationListResult.isFetching} + > + {notificationListResult.isUninitialized && listNotification()} + {notificationListResult.isFetching && ( + + )} + {!notificationListResult.isFetching && notificationListResult.error && ( + Error loading data + )} + {notificationListResult.isSuccess && ( + true} + initialValues={{ + ...notificationListResult.data, + logsToInclude: notificationListResult.data?.logsToInclude?.map((m) => ({ + label: m, + value: m, + })), + Severity: notificationListResult.data?.Severity?.map((s) => ({ + label: s, + value: s, + })), + }} + onSubmit={onSubmit} + render={({ handleSubmit, submitting, values }) => { + return ( + + {notificationConfigResult.isFetching && ( + + Loading + + )} + {notificationConfigResult.isSuccess && !notificationConfigResult.isFetching && ( + + {notificationConfigResult.data?.Results} + + )} + {notificationConfigResult.isError && !notificationConfigResult.isFetching && ( + + Could not connect to API: {notificationConfigResult.error.message} + + )} + - - - - - - - - - - - - - - - - - - - - - - - Set Notification Settings - + - - ) - }} - /> - - - )} + + + + + + + + + + + + + + + + + + + + + ) + }} + /> + )} + ) } From ef1c6c7a50547ba2c77b5cdb1abbe683c946e15e Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Mon, 22 Apr 2024 23:11:58 +0200 Subject: [PATCH 032/475] notification prettification --- src/views/cipp/app-settings/SettingsNotifications.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/views/cipp/app-settings/SettingsNotifications.jsx b/src/views/cipp/app-settings/SettingsNotifications.jsx index cf66765d185e..c22d47a8afff 100644 --- a/src/views/cipp/app-settings/SettingsNotifications.jsx +++ b/src/views/cipp/app-settings/SettingsNotifications.jsx @@ -33,7 +33,7 @@ export function SettingsNotifications() { configNotifications(values) } return ( - <> + )} - + ) } From f4db1400f20552b1b8c0139cd410729ab9d6822b Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Mon, 22 Apr 2024 23:48:08 +0200 Subject: [PATCH 033/475] upgrade extension stuff --- src/data/Extensions.json | 2 +- .../cipp/app-settings/SettingsExtensions.jsx | 183 ++++++++---------- .../app-settings/SettingsNotifications.jsx | 12 +- 3 files changed, 87 insertions(+), 110 deletions(-) diff --git a/src/data/Extensions.json b/src/data/Extensions.json index 4d31c1df3dbc..0f26e93f26a5 100644 --- a/src/data/Extensions.json +++ b/src/data/Extensions.json @@ -112,7 +112,7 @@ "type": "NinjaOne", "cat": "Documentation & Monitoring", "forceSyncButton": true, - "helpText": "NOTE: This integration requires version 5.6 of NinjaOne, which rolls out regionally between the end of November and mid-December. This integration allows you to populate custom fields with Tenant information, monitor device compliance state, document other items and generate relationships inside NinjaOne.", + "helpText": "This integration allows you to populate custom fields with Tenant information, monitor device compliance state, document other items and generate relationships inside NinjaOne.", "SettingOptions": [ { "type": "input", diff --git a/src/views/cipp/app-settings/SettingsExtensions.jsx b/src/views/cipp/app-settings/SettingsExtensions.jsx index 44569219d704..fc728407ded3 100644 --- a/src/views/cipp/app-settings/SettingsExtensions.jsx +++ b/src/views/cipp/app-settings/SettingsExtensions.jsx @@ -20,6 +20,7 @@ import { RFFCFormInput, RFFCFormSwitch } from 'src/components/forms/index.js' 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' /** * Executes various operations related to settings and extensions. @@ -44,6 +45,39 @@ export function SettingsExtensions() { values: values, }) } + + const ButtonGenerate = (integrationType, forceSync) => ( + <> + + {extensionConfigResult.isFetching && ( + + )} + Set Extension Settings + + onSubmitTest(integrationType)} className="me-2"> + {listExtensionTestResult.isFetching && ( + + )} + Test Extension + + {forceSync && ( + + execSyncExtension({ + path: 'api/ExecExtensionSync?Extension=' + integrationType, + }) + } + className="me-2" + > + {listSyncExtensionResult.isFetching && ( + + )} + Force Sync + + )} + + ) + return (
{listBackendResult.isUninitialized && listBackend({ path: 'api/ListExtensionsConfig' })} @@ -74,106 +108,59 @@ export function SettingsExtensions() { {Extensions.map((integration, idx) => ( - - - {integration.name} - - -

{integration.helpText}

- { - return ( - - - - {integration.SettingOptions.map( - (integrationOptions, idx) => - integrationOptions.type === 'input' && ( - - - - ), - )} - {integration.SettingOptions.map( - (integrationOptions, idx) => - integrationOptions.type === 'checkbox' && ( - - - - ), - )} - - - - - - {extensionConfigResult.isFetching && ( - - )} - Set Extension Settings - - onSubmitTest(integration.type)} - className="me-2" - > - {listExtensionTestResult.isFetching && ( - - )} - Test Extension - - {integration.forceSyncButton && ( - - execSyncExtension({ - path: 'api/ExecExtensionSync?Extension=' + integration.type, - }) - } - className="me-2" - > - {listSyncExtensionResult.isFetching && ( - - )} - Force Sync - + +

{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/SettingsNotifications.jsx b/src/views/cipp/app-settings/SettingsNotifications.jsx index c22d47a8afff..4dc512373b5a 100644 --- a/src/views/cipp/app-settings/SettingsNotifications.jsx +++ b/src/views/cipp/app-settings/SettingsNotifications.jsx @@ -4,17 +4,7 @@ import { } from 'src/store/api/app.js' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { faCircleNotch } from '@fortawesome/free-solid-svg-icons' -import { - CButton, - CCallout, - CCard, - CCardBody, - CCardHeader, - CCardTitle, - CCol, - CForm, - CSpinner, -} from '@coreui/react' +import { CButton, CCol, CForm, CSpinner } from '@coreui/react' import { Form, useForm } from 'react-final-form' import { RFFCFormInput, RFFCFormSwitch, RFFSelectSearch } from 'src/components/forms/index.js' import React from 'react' From 4fb8a49cec0c11f7b774ada377e2553ecee8601b Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Mon, 22 Apr 2024 23:57:07 +0200 Subject: [PATCH 034/475] prettification --- src/views/cipp/app-settings/SettingsBackend.jsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/views/cipp/app-settings/SettingsBackend.jsx b/src/views/cipp/app-settings/SettingsBackend.jsx index c57fa366997c..7b86ae472901 100644 --- a/src/views/cipp/app-settings/SettingsBackend.jsx +++ b/src/views/cipp/app-settings/SettingsBackend.jsx @@ -79,6 +79,7 @@ export function SettingsBackend() { From 4cd931256243e6377a699dbf669198b4d23bc3a7 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Mon, 22 Apr 2024 23:59:50 +0200 Subject: [PATCH 035/475] prettification --- .../cipp/app-settings/SettingsBackend.jsx | 51 ++++++++++--------- 1 file changed, 28 insertions(+), 23 deletions(-) diff --git a/src/views/cipp/app-settings/SettingsBackend.jsx b/src/views/cipp/app-settings/SettingsBackend.jsx index 7b86ae472901..3dc9a9548af5 100644 --- a/src/views/cipp/app-settings/SettingsBackend.jsx +++ b/src/views/cipp/app-settings/SettingsBackend.jsx @@ -88,29 +88,34 @@ export function SettingsBackend() { ))} - - - Cloud Shell - - -

Launch an Azure Cloud Shell Window

- - window.open( - 'https://shell.azure.com/powershell', - '_blank', - 'toolbar=no,scrollbars=yes,resizable=yes,menubar=no,location=no,status=no', - ) - } - rel="noreferrer" - > - Cloud Shell - - setVisible(true)} className="mb-3"> - Command Reference - -
-
+ + {' '} + + window.open( + 'https://shell.azure.com/powershell', + '_blank', + 'toolbar=no,scrollbars=yes,resizable=yes,menubar=no,location=no,status=no', + ) + } + rel="noreferrer" + > + Cloud Shell + + setVisible(true)} className="me-2"> + Command Reference + + + } + > +

Launch an Azure Cloud Shell Window

+
Date: Tue, 23 Apr 2024 01:00:51 +0200 Subject: [PATCH 036/475] add securescore to menu --- src/_nav.jsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/_nav.jsx b/src/_nav.jsx index fbf1ff50f013..26f7e0764890 100644 --- a/src/_nav.jsx +++ b/src/_nav.jsx @@ -142,6 +142,11 @@ const _nav = [ name: 'Enterprise Applications', to: '/tenant/administration/enterprise-apps', }, + { + component: CNavItem, + name: 'Secure Score', + to: '/tenant/administration/securescore', + }, { component: CNavItem, name: 'App Consent Requests', From 2910da61148fe5c3a5c5b740f5b6f558cdf6a1a3 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Tue, 23 Apr 2024 12:00:03 +0200 Subject: [PATCH 037/475] test in dev --- src/components/forms/RFFComponents.jsx | 10 +- .../SettingsExtensionMappings.jsx | 176 ++++++++++++------ 2 files changed, 129 insertions(+), 57 deletions(-) diff --git a/src/components/forms/RFFComponents.jsx b/src/components/forms/RFFComponents.jsx index 234e990ca5c5..c1880eee88b8 100644 --- a/src/components/forms/RFFComponents.jsx +++ b/src/components/forms/RFFComponents.jsx @@ -444,6 +444,12 @@ export const RFFSelectSearch = ({ return ( {({ meta, input }) => { + const handleChange = onChange + ? (e) => { + input.onChange(e) + onChange(e) + } + : input.onChange return (
@@ -473,7 +479,7 @@ export const RFFSelectSearch = ({ options={selectSearchvalues} placeholder={placeholder} isMulti={multi} - onChange={onChange} + onChange={handleChange} onInputChange={debounceOnInputChange} inputValue={inputText} isLoading={isLoading} @@ -510,7 +516,7 @@ export const RFFSelectSearch = ({ options={selectSearchvalues} placeholder={placeholder} isMulti={multi} - onChange={onChange} + onChange={handleChange} onInputChange={debounceOnInputChange} inputValue={inputText} isLoading={isLoading} diff --git a/src/views/cipp/app-settings/SettingsExtensionMappings.jsx b/src/views/cipp/app-settings/SettingsExtensionMappings.jsx index 5386afbf1fbd..18586d6a1e79 100644 --- a/src/views/cipp/app-settings/SettingsExtensionMappings.jsx +++ b/src/views/cipp/app-settings/SettingsExtensionMappings.jsx @@ -9,6 +9,7 @@ import { CCardTitle, CCol, CForm, + CRow, CSpinner, } from '@coreui/react' import { Form } from 'react-final-form' @@ -17,6 +18,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { faCircleNotch } from '@fortawesome/free-solid-svg-icons' import React from 'react' import { CippCallout } from 'src/components/layout/index.js' +import CippButtonCard from 'src/components/contentcards/CippButtonCard' /** * Retrieves and sets the extension mappings for HaloPSA and NinjaOne. @@ -65,6 +67,59 @@ export function SettingsExtensionMappings() { values: { mappings: values }, }) } + + const [addedAttributes, setAddedAttribute] = React.useState(1) + const [mappingArray, setMappingArray] = React.useState({ HaloPSA: [], NinjaOrgs: [] }) + + const MappingRow = ({ integrationType, index, tenantData, clientData, addButton = true }) => ( + + + ({ + name: tenant.displayName, + value: tenant.customerId, + }))} + //set the name of the other field, to the value of this field by using mappingArray, each time we add a new row, we add a new object to the array. + onChange={(e) => { + console.log(mappingArray[integrationType][index]) + mappingArray[integrationType][index] = { tenant: e.value } + setMappingArray({ ...mappingArray }) + //also complete the normal onChange event + }} + /> + + + + + + ({ + name: client.name, + value: client.value, + }))} + placeholder="Select a HaloPSA Client" + /> + + {addButton && ( + setAddedAttribute(addedAttributes + 1)} + className={`my-4 circular-button`} + title={'+'} + > + + + )} + + ) + return (
{listBackendHaloResult.isUninitialized && @@ -74,61 +129,72 @@ export function SettingsExtensionMappings() { {listBackendNinjaFieldsResult.isUninitialized && listNinjaFieldsBackend({ path: 'api/ExecExtensionMapping?List=NinjaFields' })} <> - - - HaloPSA Mapping Table - - - {listBackendHaloResult.isFetching ? ( - - ) : ( - { - return ( - - - Use the table below to map your client to the correct PSA client - {listBackendHaloResult.isSuccess && - listBackendHaloResult.data.Tenants?.map((tenant) => ( - - ))} - - - - {extensionHaloConfigResult.isFetching && ( - - )} - Set Mappings - - {(extensionHaloConfigResult.isSuccess || - extensionHaloConfigResult.isError) && - !extensionHaloConfigResult.isFetching && ( - - {extensionHaloConfigResult.isSuccess - ? extensionHaloConfigResult.data.Results - : 'Error'} - - )} - - - ) - }} - /> - )} - - + + {extensionHaloConfigResult.isFetching && ( + + )} + Set Mappings + + } + > + {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. + listBackendHaloResult.isSuccess && + listBackendHaloResult.data.HaloClients.map((HaloClient, idx) => + MappingRow({ + integrationType: 'HaloPSA', + index: idx, + clientData: listBackendHaloResult.data.HaloClients, + tenantData: listBackendHaloResult.data.Tenants, + addButton: false, + }), + ) + } + {[...Array(addedAttributes)].map((currentItem, idx) => + MappingRow({ + integrationType: 'HaloPSA', + index: 10000 + idx, //we add 10000 to the index to not conflict with the existing mappings + clientData: listBackendHaloResult.data.HaloClients, + tenantData: listBackendHaloResult.data.Tenants, + }), + )} + + + {(extensionHaloConfigResult.isSuccess || extensionHaloConfigResult.isError) && + !extensionHaloConfigResult.isFetching && ( + + {extensionHaloConfigResult.isSuccess + ? extensionHaloConfigResult.data.Results + : 'Error'} + + )} + + + ) + }} + /> + )} + NinjaOne Field Mapping Table From fa407a3f3064ebae791551b60aa701608ced18ec Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Tue, 23 Apr 2024 13:52:40 +0200 Subject: [PATCH 038/475] testing with new layout --- .../SettingsExtensionMappings.jsx | 358 ++++++++++-------- 1 file changed, 196 insertions(+), 162 deletions(-) diff --git a/src/views/cipp/app-settings/SettingsExtensionMappings.jsx b/src/views/cipp/app-settings/SettingsExtensionMappings.jsx index 18586d6a1e79..a1181521ba25 100644 --- a/src/views/cipp/app-settings/SettingsExtensionMappings.jsx +++ b/src/views/cipp/app-settings/SettingsExtensionMappings.jsx @@ -1,17 +1,5 @@ import { useLazyGenericGetRequestQuery, useLazyGenericPostRequestQuery } from 'src/store/api/app.js' -import { - CButton, - CCallout, - CCard, - CCardBody, - CCardHeader, - CCardText, - CCardTitle, - CCol, - CForm, - CRow, - CSpinner, -} from '@coreui/react' +import { 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' @@ -19,6 +7,8 @@ import { faCircleNotch } from '@fortawesome/free-solid-svg-icons' import React from 'react' import { CippCallout } from 'src/components/layout/index.js' import CippButtonCard from 'src/components/contentcards/CippButtonCard' +import { CippTable } from 'src/components/tables' +import { CellTip } from 'src/components/tables/CellGenericFormat' /** * Retrieves and sets the extension mappings for HaloPSA and NinjaOne. @@ -69,59 +59,55 @@ export function SettingsExtensionMappings() { } const [addedAttributes, setAddedAttribute] = React.useState(1) - const [mappingArray, setMappingArray] = React.useState({ HaloPSA: [], NinjaOrgs: [] }) + const [mappingArray, setMappingArray] = React.useState('defaultMapping') - const MappingRow = ({ integrationType, index, tenantData, clientData, addButton = true }) => ( - - - ({ - name: tenant.displayName, - value: tenant.customerId, - }))} - //set the name of the other field, to the value of this field by using mappingArray, each time we add a new row, we add a new object to the array. - onChange={(e) => { - console.log(mappingArray[integrationType][index]) - mappingArray[integrationType][index] = { tenant: e.value } - setMappingArray({ ...mappingArray }) - //also complete the normal onChange event - }} - /> - - - - - - ({ - name: client.name, - value: client.value, - }))} - placeholder="Select a HaloPSA Client" - /> - - {addButton && ( - setAddedAttribute(addedAttributes + 1)} - className={`my-4 circular-button`} - title={'+'} - > - - - )} - - ) + const Offcanvas = (row, rowIndex, formatExtraData) => { + return ( + <> + + console.log('Remove Mapping')} + > + + + + + ) + } + const columns = [ + { + name: 'Tenant', + selector: (row) => row['Tenant'], + sortable: true, + cell: (row) => CellTip(row['Tenant']), + exportSelector: 'Tenant', + }, + { + name: 'Halo Client Name', + selector: (row) => row['haloName'], + sortable: true, + cell: (row) => CellTip(row['haloName']), + exportSelector: 'haloName', + }, + { + name: 'Halo ID', + selector: (row) => row['haloId'], + sortable: true, + cell: (row) => CellTip(row['haloId']), + exportSelector: 'haloId', + }, + { + name: 'Actions', + cell: Offcanvas, + maxWidth: '80px', + }, + ] return ( -
+ {listBackendHaloResult.isUninitialized && listHaloBackend({ path: 'api/ExecExtensionMapping?List=Halo' })} {listBackendNinjaOrgsResult.isUninitialized && @@ -129,77 +115,126 @@ export function SettingsExtensionMappings() { {listBackendNinjaFieldsResult.isUninitialized && listNinjaFieldsBackend({ path: 'api/ExecExtensionMapping?List=NinjaFields' })} <> - - {extensionHaloConfigResult.isFetching && ( - - )} - Set Mappings - - } - > - {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. - listBackendHaloResult.isSuccess && - listBackendHaloResult.data.HaloClients.map((HaloClient, idx) => - MappingRow({ - integrationType: 'HaloPSA', - index: idx, - clientData: listBackendHaloResult.data.HaloClients, - tenantData: listBackendHaloResult.data.Tenants, - addButton: false, - }), + + + {extensionHaloConfigResult.isFetching && ( + + )} + Save Mappings + + } + > + {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 && ( + ({ + Tenant: key, + haloName: listBackendHaloResult.data?.Mappings[key].label, + haloId: listBackendHaloResult.data?.Mappings[key].value, + }), + )} + isModal={true} + /> ) - } - {[...Array(addedAttributes)].map((currentItem, idx) => - MappingRow({ - integrationType: 'HaloPSA', - index: 10000 + idx, //we add 10000 to the index to not conflict with the existing mappings - clientData: listBackendHaloResult.data.HaloClients, - tenantData: listBackendHaloResult.data.Tenants, - }), - )} - - - {(extensionHaloConfigResult.isSuccess || extensionHaloConfigResult.isError) && - !extensionHaloConfigResult.isFetching && ( - + + ({ + name: tenant.displayName, + value: tenant.customerId, + }))} + //set the name of the other field, to the value of this field by using mappingArray, each time we add a new row, we add a new object to the array. + onChange={(e) => { + setMappingArray(e.value) + }} + /> + + + + + + ({ + name: client.name, + value: client.value, + }))} + placeholder="Select a HaloPSA Client" + /> + + setAddedAttribute(addedAttributes + 1)} + className={`my-4 circular-button`} + title={'+'} > - {extensionHaloConfigResult.isSuccess - ? extensionHaloConfigResult.data.Results - : 'Error'} - - )} - - - ) - }} - /> - )} - - - - NinjaOne Field Mapping Table - - + + + + + + {(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. + + + ) + }} + /> + )} + + + + + {extensionNinjaFieldsConfigResult.isFetching && ( + + )} + Set Mappings + + } + > {listBackendNinjaFieldsResult.isFetching ? ( ) : ( @@ -208,7 +243,7 @@ export function SettingsExtensionMappings() { initialValues={listBackendNinjaFieldsResult.data?.Mappings} render={({ handleSubmit, submitting, values }) => { return ( - +
Organization Global Custom Field Mapping

@@ -249,12 +284,6 @@ export function SettingsExtensionMappings() { ))} - - {extensionNinjaFieldsConfigResult.isFetching && ( - - )} - Set Mappings - {(extensionNinjaFieldsConfigResult.isSuccess || extensionNinjaFieldsConfigResult.isError) && !extensionNinjaFieldsConfigResult.isFetching && ( @@ -276,13 +305,30 @@ export function SettingsExtensionMappings() { }} /> )} - - - - - NinjaOne Organization Mapping Table - - + + + + + + {extensionNinjaOrgsConfigResult.isFetching && ( + + )} + Set Mappings + + onNinjaOrgsAutomap()} className="me-2"> + {extensionNinjaOrgsAutomapResult.isFetching && ( + + )} + Automap NinjaOne Organizations + + + } + > {listBackendNinjaOrgsResult.isFetching ? ( ) : ( @@ -291,7 +337,7 @@ export function SettingsExtensionMappings() { initialValues={listBackendNinjaOrgsResult.data?.Mappings} render={({ handleSubmit, submitting, values }) => { return ( - + Use the table below to map your client to the correct NinjaOne Organization {listBackendNinjaOrgsResult.isSuccess && @@ -306,18 +352,6 @@ export function SettingsExtensionMappings() { ))} - - {extensionNinjaOrgsConfigResult.isFetching && ( - - )} - Set Mappings - - onNinjaOrgsAutomap()} className="me-2"> - {extensionNinjaOrgsAutomapResult.isFetching && ( - - )} - Automap NinjaOne Organizations - {(extensionNinjaOrgsConfigResult.isSuccess || extensionNinjaOrgsConfigResult.isError) && !extensionNinjaFieldsConfigResult.isFetching && ( @@ -349,9 +383,9 @@ export function SettingsExtensionMappings() { }} /> )} - - + + -

+ ) } From e5116eb39664c791057ac519dd62eee14efa3a23 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Tue, 23 Apr 2024 15:33:00 +0200 Subject: [PATCH 039/475] new mapping experience. --- .../SettingsExtensionMappings.jsx | 69 ++++++++++++++----- 1 file changed, 52 insertions(+), 17 deletions(-) diff --git a/src/views/cipp/app-settings/SettingsExtensionMappings.jsx b/src/views/cipp/app-settings/SettingsExtensionMappings.jsx index a1181521ba25..5e3f6267829d 100644 --- a/src/views/cipp/app-settings/SettingsExtensionMappings.jsx +++ b/src/views/cipp/app-settings/SettingsExtensionMappings.jsx @@ -4,7 +4,7 @@ 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 from 'react' +import React, { useEffect } from 'react' import { CippCallout } from 'src/components/layout/index.js' import CippButtonCard from 'src/components/contentcards/CippButtonCard' import { CippTable } from 'src/components/tables' @@ -16,6 +16,10 @@ import { CellTip } from 'src/components/tables/CellGenericFormat' * @returns {JSX.Element} - JSX component representing the settings extension mappings. */ export function SettingsExtensionMappings() { + const [addedAttributes, setAddedAttribute] = React.useState(1) + const [mappingArray, setMappingArray] = React.useState('defaultMapping') + const [mappingValue, setMappingValue] = React.useState({}) + const [haloMappingsArray, setHaloMappingsArray] = React.useState([]) const [listHaloBackend, listBackendHaloResult = []] = useLazyGenericGetRequestQuery() const [listNinjaOrgsBackend, listBackendNinjaOrgsResult] = useLazyGenericGetRequestQuery() const [listNinjaFieldsBackend, listBackendNinjaFieldsResult] = useLazyGenericGetRequestQuery() @@ -27,10 +31,15 @@ export function SettingsExtensionMappings() { const [setNinjaFieldsExtensionconfig, extensionNinjaFieldsConfigResult] = useLazyGenericPostRequestQuery() - const onHaloSubmit = (values) => { + const onHaloSubmit = () => { + console.log(haloMappingsArray) + const originalFormat = haloMappingsArray.reduce((acc, item) => { + acc[item.Tenant?.customerId] = { label: item.haloName, value: item.haloId } + return acc + }, {}) setHaloExtensionconfig({ path: 'api/ExecExtensionMapping?AddMapping=Halo', - values: { mappings: values }, + values: { mappings: originalFormat }, }) } const onNinjaOrgsSubmit = (values) => { @@ -58,8 +67,17 @@ export function SettingsExtensionMappings() { }) } - const [addedAttributes, setAddedAttribute] = React.useState(1) - const [mappingArray, setMappingArray] = React.useState('defaultMapping') + useEffect(() => { + if (listBackendHaloResult.isSuccess) { + setHaloMappingsArray( + Object.keys(listBackendHaloResult.data?.Mappings).map((key) => ({ + Tenant: listBackendHaloResult.data?.Tenants.find((tenant) => tenant.customerId === key), + haloName: listBackendHaloResult.data?.Mappings[key].label, + haloId: listBackendHaloResult.data?.Mappings[key].value, + })), + ) + } + }, [listBackendHaloResult.isSuccess]) const Offcanvas = (row, rowIndex, formatExtraData) => { return ( @@ -69,7 +87,11 @@ export function SettingsExtensionMappings() { size="sm" variant="ghost" color="danger" - onClick={() => console.log('Remove Mapping')} + onClick={() => + setHaloMappingsArray((currentHaloMappings) => + currentHaloMappings.filter((item) => item !== row), + ) + } > @@ -80,11 +102,18 @@ export function SettingsExtensionMappings() { const columns = [ { name: 'Tenant', - selector: (row) => row['Tenant'], + selector: (row) => row.Tenant?.displayName, sortable: true, - cell: (row) => CellTip(row['Tenant']), + cell: (row) => CellTip(row.Tenant?.displayName), exportSelector: 'Tenant', }, + { + name: 'TenantId', + selector: (row) => row.Tenant?.customerId, + sortable: true, + exportSelector: 'Tenant/customerId', + omit: true, + }, { name: 'Halo Client Name', selector: (row) => row['haloName'], @@ -147,13 +176,7 @@ export function SettingsExtensionMappings() { showFilter={true} reportName="none" columns={columns} - data={Object.keys(listBackendHaloResult.data?.Mappings).map( - (key) => ({ - Tenant: key, - haloName: listBackendHaloResult.data?.Mappings[key].label, - haloId: listBackendHaloResult.data?.Mappings[key].value, - }), - )} + data={haloMappingsArray} isModal={true} /> ) @@ -167,7 +190,6 @@ export function SettingsExtensionMappings() { name: tenant.displayName, value: tenant.customerId, }))} - //set the name of the other field, to the value of this field by using mappingArray, each time we add a new row, we add a new object to the array. onChange={(e) => { setMappingArray(e.value) }} @@ -183,11 +205,24 @@ export function SettingsExtensionMappings() { name: client.name, value: client.value, }))} + onChange={(e) => setMappingValue(e)} placeholder="Select a HaloPSA Client" /> setAddedAttribute(addedAttributes + 1)} + onClick={() => + //set the new mapping in the array + setHaloMappingsArray([ + ...haloMappingsArray, + { + Tenant: listBackendHaloResult.data?.Tenants.find( + (tenant) => tenant.customerId === mappingArray, + ), + haloName: mappingValue.label, + haloId: mappingValue.value, + }, + ]) + } className={`my-4 circular-button`} title={'+'} > From 623007891a318285e611bca83b09438f757949cd Mon Sep 17 00:00:00 2001 From: John Duprey Date: Tue, 23 Apr 2024 10:19:09 -0400 Subject: [PATCH 040/475] filter available relationships for onboarding --- .../tenant/administration/TenantOnboardingWizard.jsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/views/tenant/administration/TenantOnboardingWizard.jsx b/src/views/tenant/administration/TenantOnboardingWizard.jsx index a6eb19460e55..122765dff6c3 100644 --- a/src/views/tenant/administration/TenantOnboardingWizard.jsx +++ b/src/views/tenant/administration/TenantOnboardingWizard.jsx @@ -312,14 +312,14 @@ const TenantOnboardingWizard = () => { reportName="Add-GDAP-Relationship" keyField="id" path="/api/ListGraphRequest" - params={{ Endpoint: 'tenantRelationships/delegatedAdminRelationships' }} + params={{ + Endpoint: 'tenantRelationships/delegatedAdminRelationships', + $filter: + "(status eq 'active' or status eq 'approvalPending') and not startsWith(displayName,'MLT_')", + }} columns={columns} filterlist={[ { filterName: 'Active Relationships', filter: 'Complex: status eq active' }, - { - filterName: 'Terminated Relationships', - filter: 'Complex: status eq terminated', - }, { filterName: 'Pending Relationships', filter: 'Complex: status eq approvalPending', From 6605f34a18b07a95812d91353e815a1d3fb30a19 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Tue, 23 Apr 2024 16:31:29 +0200 Subject: [PATCH 041/475] text change --- src/views/cipp/app-settings/SettingsExtensionMappings.jsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/views/cipp/app-settings/SettingsExtensionMappings.jsx b/src/views/cipp/app-settings/SettingsExtensionMappings.jsx index 5e3f6267829d..537c46500487 100644 --- a/src/views/cipp/app-settings/SettingsExtensionMappings.jsx +++ b/src/views/cipp/app-settings/SettingsExtensionMappings.jsx @@ -246,8 +246,9 @@ export function SettingsExtensionMappings() { )} + After editing the mappings you must click Save Mappings for the changes to - take effect. + take effect. The table will be saved exactly as presented. ) From b8fd9bf5433007fab95e8c804bac5f27fb3b1e45 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Tue, 23 Apr 2024 18:05:07 +0200 Subject: [PATCH 042/475] switches orders --- .../SettingsExtensionMappings.jsx | 157 +++++++++--------- 1 file changed, 79 insertions(+), 78 deletions(-) diff --git a/src/views/cipp/app-settings/SettingsExtensionMappings.jsx b/src/views/cipp/app-settings/SettingsExtensionMappings.jsx index 537c46500487..e29d6bd5198e 100644 --- a/src/views/cipp/app-settings/SettingsExtensionMappings.jsx +++ b/src/views/cipp/app-settings/SettingsExtensionMappings.jsx @@ -257,6 +257,85 @@ export function SettingsExtensionMappings() { )} + + {' '} + + + {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 + {listBackendNinjaOrgsResult.isSuccess && + listBackendNinjaOrgsResult.data.Tenants.map((tenant) => ( + + ))} + + + {(extensionNinjaOrgsConfigResult.isSuccess || + extensionNinjaOrgsConfigResult.isError) && + !extensionNinjaFieldsConfigResult.isFetching && ( + + {extensionNinjaOrgsConfigResult.isSuccess + ? extensionNinjaOrgsConfigResult.data.Results + : 'Error'} + + )} + {(extensionNinjaOrgsAutomapResult.isSuccess || + extensionNinjaOrgsAutomapResult.isError) && ( + + {extensionNinjaOrgsAutomapResult.isSuccess + ? extensionNinjaOrgsAutomapResult.data.Results + : 'Error'} + + )} + + + ) + }} + /> + )} + + - - - - {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 - {listBackendNinjaOrgsResult.isSuccess && - listBackendNinjaOrgsResult.data.Tenants.map((tenant) => ( - - ))} - - - {(extensionNinjaOrgsConfigResult.isSuccess || - extensionNinjaOrgsConfigResult.isError) && - !extensionNinjaFieldsConfigResult.isFetching && ( - - {extensionNinjaOrgsConfigResult.isSuccess - ? extensionNinjaOrgsConfigResult.data.Results - : 'Error'} - - )} - {(extensionNinjaOrgsAutomapResult.isSuccess || - extensionNinjaOrgsAutomapResult.isError) && ( - - {extensionNinjaOrgsAutomapResult.isSuccess - ? extensionNinjaOrgsAutomapResult.data.Results - : 'Error'} - - )} - - - ) - }} - /> - )} - - ) From e44b89d7bd2f6fc9d548837a246ff7aab3b56204 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Tue, 23 Apr 2024 13:40:16 -0400 Subject: [PATCH 043/475] auto refresh for recent jobs --- src/components/layout/AppHeader.jsx | 77 +++++++++++++------ .../utilities/CippActionsOffcanvas.jsx | 2 +- 2 files changed, 56 insertions(+), 23 deletions(-) diff --git a/src/components/layout/AppHeader.jsx b/src/components/layout/AppHeader.jsx index 85fe747dd98f..9661c89e7eba 100644 --- a/src/components/layout/AppHeader.jsx +++ b/src/components/layout/AppHeader.jsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from 'react' +import React, { useState, useEffect, useRef } from 'react' import { useSelector, useDispatch } from 'react-redux' import { CAlert, @@ -72,8 +72,29 @@ const AppHeader = () => { loadCippQueue() } + 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]) + } + useEffect(() => { - if (cippQueueList.isFetching || cippQueueList.isLoading) { + if (cippQueueList.isUninitialized && (cippQueueList.isFetching || cippQueueList.isLoading)) { setCippQueueExtendedInfo([ { label: 'Fetching recent jobs', @@ -82,28 +103,40 @@ const AppHeader = () => { link: '#', }, ]) - } - if ( - cippQueueList.isSuccess && - Array.isArray(cippQueueList.data) && - cippQueueList.data.length > 0 - ) { - setCippQueueExtendedInfo( - cippQueueList.data?.map((job) => ({ - label: `${job.Name}`, - value: job.Status, - link: job.Link, - timestamp: job.Timestamp, - percent: job.PercentComplete, - progressText: `${job.PercentComplete}%`, - })), - ) } else { - setCippQueueExtendedInfo([ - { label: 'No jobs to display', value: '', timestamp: Date(), link: '#' }, - ]) + if ( + cippQueueList.isSuccess && + Array.isArray(cippQueueList.data) && + cippQueueList.data.length > 0 + ) { + setCippQueueExtendedInfo( + cippQueueList.data?.map((job) => ({ + label: `${job.Name}`, + value: job.Status, + link: job.Link, + timestamp: job.Timestamp, + percent: job.PercentComplete, + progressText: `${job.PercentComplete}%`, + })), + ) + } else { + setCippQueueExtendedInfo([ + { label: 'No jobs to display', value: '', timestamp: Date(), link: '#' }, + ]) + } } - }, [cippQueueList]) + }, [cippQueueList, setCippQueueExtendedInfo]) + + useInterval( + async () => { + if (cippQueueVisible) { + setCippQueueRefresh((Math.random() + 1).toString(36).substring(7)) + getCippQueueList({ path: 'api/ListCippQueue', params: { refresh: cippQueueRefresh } }) + } + }, + 5000, + cippQueueVisible, + ) const SwitchTheme = () => { let targetTheme = preferredTheme diff --git a/src/components/utilities/CippActionsOffcanvas.jsx b/src/components/utilities/CippActionsOffcanvas.jsx index dc9567479ab5..f67e3d11ed88 100644 --- a/src/components/utilities/CippActionsOffcanvas.jsx +++ b/src/components/utilities/CippActionsOffcanvas.jsx @@ -231,7 +231,7 @@ export default function CippActionsOffcanvas(props) { - {action.percent && ( + {action?.percent > 0 && (
From 8dd57ad48bc86d068aa69cb33e21819d256f588e Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Tue, 23 Apr 2024 20:00:49 +0200 Subject: [PATCH 044/475] Ninjaone mapping changes --- .../SettingsExtensionMappings.jsx | 177 +++++++++++++++--- 1 file changed, 150 insertions(+), 27 deletions(-) diff --git a/src/views/cipp/app-settings/SettingsExtensionMappings.jsx b/src/views/cipp/app-settings/SettingsExtensionMappings.jsx index e29d6bd5198e..6a1a318894b4 100644 --- a/src/views/cipp/app-settings/SettingsExtensionMappings.jsx +++ b/src/views/cipp/app-settings/SettingsExtensionMappings.jsx @@ -20,6 +20,8 @@ export function SettingsExtensionMappings() { const [mappingArray, setMappingArray] = React.useState('defaultMapping') const [mappingValue, setMappingValue] = React.useState({}) const [haloMappingsArray, setHaloMappingsArray] = React.useState([]) + const [ninjaMappingsArray, setNinjaMappingsArray] = React.useState([]) + const [listHaloBackend, listBackendHaloResult = []] = useLazyGenericGetRequestQuery() const [listNinjaOrgsBackend, listBackendNinjaOrgsResult] = useLazyGenericGetRequestQuery() const [listNinjaFieldsBackend, listBackendNinjaFieldsResult] = useLazyGenericGetRequestQuery() @@ -32,7 +34,6 @@ export function SettingsExtensionMappings() { useLazyGenericPostRequestQuery() const onHaloSubmit = () => { - console.log(haloMappingsArray) const originalFormat = haloMappingsArray.reduce((acc, item) => { acc[item.Tenant?.customerId] = { label: item.haloName, value: item.haloId } return acc @@ -43,6 +44,10 @@ export function SettingsExtensionMappings() { }) } const onNinjaOrgsSubmit = (values) => { + const originalFormat = ninjaMappingsArray.reduce((acc, item) => { + acc[item.Tenant?.customerId] = { label: item.haloName, value: item.haloId } + return acc + }, {}) setNinjaOrgsExtensionconfig({ path: 'api/ExecExtensionMapping?AddMapping=NinjaOrgs', values: { mappings: values }, @@ -79,6 +84,24 @@ export function SettingsExtensionMappings() { } }, [listBackendHaloResult.isSuccess]) + useEffect(() => { + if (listBackendNinjaOrgsResult.isSuccess) { + setNinjaMappingsArray( + Object.keys(listBackendNinjaOrgsResult.data?.Mappings).map((key) => ({ + Tenant: listBackendNinjaOrgsResult.data?.Tenants.find( + (tenant) => tenant.customerId === key, + ), + ninjaName: listBackendNinjaOrgsResult.data?.Mappings[key].label, + ninjaId: listBackendNinjaOrgsResult.data?.Mappings[key].value, + })), + ) + } + }, [ + listBackendNinjaOrgsResult.data?.Mappings, + listBackendNinjaOrgsResult.data?.Tenants, + listBackendNinjaOrgsResult.isSuccess, + ]) + const Offcanvas = (row, rowIndex, formatExtraData) => { return ( <> @@ -88,9 +111,13 @@ export function SettingsExtensionMappings() { variant="ghost" color="danger" onClick={() => - setHaloMappingsArray((currentHaloMappings) => - currentHaloMappings.filter((item) => item !== row), - ) + row.haloId + ? setHaloMappingsArray((currentHaloMappings) => + currentHaloMappings.filter((item) => item !== row), + ) + : setNinjaMappingsArray((currentNinjaMappings) => + currentNinjaMappings.filter((item) => item !== row), + ) } > @@ -99,7 +126,7 @@ export function SettingsExtensionMappings() { ) } - const columns = [ + const halocolumns = [ { name: 'Tenant', selector: (row) => row.Tenant?.displayName, @@ -135,6 +162,42 @@ export function SettingsExtensionMappings() { }, ] + const ninjacolumns = [ + { + name: 'Tenant', + selector: (row) => row.Tenant?.displayName, + sortable: true, + cell: (row) => CellTip(row.Tenant?.displayName), + exportSelector: 'Tenant', + }, + { + name: 'TenantId', + selector: (row) => row.Tenant?.customerId, + sortable: true, + exportSelector: 'Tenant/customerId', + omit: true, + }, + { + name: 'NinjaOne Organization Name', + selector: (row) => row['ninjaName'], + sortable: true, + cell: (row) => CellTip(row['ninjaName']), + exportSelector: 'ninjaName', + }, + { + name: 'NinjaOne Organization ID', + selector: (row) => row['ninjaId'], + sortable: true, + cell: (row) => CellTip(row['ninjaId']), + exportSelector: 'ninjaId', + }, + { + name: 'Actions', + cell: Offcanvas, + maxWidth: '80px', + }, + ] + return ( {listBackendHaloResult.isUninitialized && @@ -175,7 +238,7 @@ export function SettingsExtensionMappings() { @@ -285,27 +348,92 @@ export function SettingsExtensionMappings() { ) : ( { return ( - Use the table below to map your client to the correct NinjaOne Organization - {listBackendNinjaOrgsResult.isSuccess && - listBackendNinjaOrgsResult.data.Tenants.map((tenant) => ( + 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 + setNinjaMappingsArray([ + ...ninjaMappingsArray, + { + Tenant: listBackendNinjaOrgsResult.data?.Tenants.find( + (tenant) => tenant.customerId === mappingArray, + ), + ninjaName: mappingValue.label, + ninjaId: mappingValue.value, + }, + ]) + } + className={`my-4 circular-button`} + title={'+'} + > + + + + {(extensionNinjaOrgsAutomapResult.isSuccess || + extensionNinjaOrgsAutomapResult.isError) && + !extensionNinjaOrgsAutomapResult.isFetching && ( + + {extensionNinjaOrgsAutomapResult.isSuccess + ? extensionNinjaOrgsAutomapResult.data.Results + : 'Error'} + + )} {(extensionNinjaOrgsConfigResult.isSuccess || extensionNinjaOrgsConfigResult.isError) && - !extensionNinjaFieldsConfigResult.isFetching && ( + !extensionNinjaOrgsConfigResult.isFetching && ( )} - {(extensionNinjaOrgsAutomapResult.isSuccess || - extensionNinjaOrgsAutomapResult.isError) && ( - - {extensionNinjaOrgsAutomapResult.isSuccess - ? extensionNinjaOrgsAutomapResult.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. + ) }} From 91d04dbe67ce351df3d38f2151e8cb8d317fce1a Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Tue, 23 Apr 2024 20:38:41 +0200 Subject: [PATCH 045/475] corrected mapping send --- src/views/cipp/app-settings/SettingsExtensionMappings.jsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/views/cipp/app-settings/SettingsExtensionMappings.jsx b/src/views/cipp/app-settings/SettingsExtensionMappings.jsx index 6a1a318894b4..890463fa65e2 100644 --- a/src/views/cipp/app-settings/SettingsExtensionMappings.jsx +++ b/src/views/cipp/app-settings/SettingsExtensionMappings.jsx @@ -43,14 +43,15 @@ export function SettingsExtensionMappings() { values: { mappings: originalFormat }, }) } - const onNinjaOrgsSubmit = (values) => { + const onNinjaOrgsSubmit = () => { const originalFormat = ninjaMappingsArray.reduce((acc, item) => { acc[item.Tenant?.customerId] = { label: item.haloName, value: item.haloId } return acc }, {}) + setNinjaOrgsExtensionconfig({ path: 'api/ExecExtensionMapping?AddMapping=NinjaOrgs', - values: { mappings: values }, + values: { mappings: originalFormat }, }) } From 1562bc63f5848d4fd1e4f4492f22b9d027fc4e95 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Tue, 23 Apr 2024 20:44:58 +0200 Subject: [PATCH 046/475] corrected shipped info --- src/views/cipp/app-settings/SettingsExtensionMappings.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/cipp/app-settings/SettingsExtensionMappings.jsx b/src/views/cipp/app-settings/SettingsExtensionMappings.jsx index 890463fa65e2..5e7c600abd22 100644 --- a/src/views/cipp/app-settings/SettingsExtensionMappings.jsx +++ b/src/views/cipp/app-settings/SettingsExtensionMappings.jsx @@ -45,7 +45,7 @@ export function SettingsExtensionMappings() { } const onNinjaOrgsSubmit = () => { const originalFormat = ninjaMappingsArray.reduce((acc, item) => { - acc[item.Tenant?.customerId] = { label: item.haloName, value: item.haloId } + acc[item.Tenant?.customerId] = { label: item.ninjaName, value: item.ninjaId } return acc }, {}) From 8fdecaf159bc6d88b96ec8fccc539c6d67f6da4a Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Tue, 23 Apr 2024 21:30:33 +0200 Subject: [PATCH 047/475] automapping --- .../SettingsExtensionMappings.jsx | 41 ++++++++++++++++--- 1 file changed, 35 insertions(+), 6 deletions(-) diff --git a/src/views/cipp/app-settings/SettingsExtensionMappings.jsx b/src/views/cipp/app-settings/SettingsExtensionMappings.jsx index 5e7c600abd22..aa3191f933b9 100644 --- a/src/views/cipp/app-settings/SettingsExtensionMappings.jsx +++ b/src/views/cipp/app-settings/SettingsExtensionMappings.jsx @@ -73,6 +73,27 @@ export function SettingsExtensionMappings() { }) } + const onHaloAutomap = (values) => { + console.log('starting automap') + //check for a match between the tenant and the halo client based on the tenants displayName property, if so, create the mapping and add to the array. + const newMappings = listBackendHaloResult.data?.Tenants.map( + (tenant) => { + const haloClient = listBackendHaloResult.data?.HaloClients.find( + (client) => client.name === tenant.displayName, + ) + if (haloClient) { + return { + tenant: tenant.customerId, + haloClient: haloClient.label, + haloId: haloClient.value, + } + } + }, + //filter out any undefined values + ).filter((item) => item !== undefined) + setHaloMappingsArray((currentHaloMappings) => [...currentHaloMappings, ...newMappings]) + } + useEffect(() => { if (listBackendHaloResult.isSuccess) { setHaloMappingsArray( @@ -214,12 +235,20 @@ export function SettingsExtensionMappings() { titleType="big" isFetching={listHaloBackend.isFetching} CardButton={ - - {extensionHaloConfigResult.isFetching && ( - - )} - Save Mappings - + <> + + {extensionHaloConfigResult.isFetching && ( + + )} + Save Mappings + + onHaloAutomap()} className="me-2"> + {extensionNinjaOrgsAutomapResult.isFetching && ( + + )} + Automap HaloPSA Clients + + } > {listBackendHaloResult.isFetching ? ( From 2441b136bc12498785726a3cebf0a125c707d70d Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Tue, 23 Apr 2024 21:34:22 +0200 Subject: [PATCH 048/475] automapping fixes --- src/views/cipp/app-settings/SettingsExtensionMappings.jsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/views/cipp/app-settings/SettingsExtensionMappings.jsx b/src/views/cipp/app-settings/SettingsExtensionMappings.jsx index aa3191f933b9..06410dc9fad2 100644 --- a/src/views/cipp/app-settings/SettingsExtensionMappings.jsx +++ b/src/views/cipp/app-settings/SettingsExtensionMappings.jsx @@ -82,6 +82,8 @@ export function SettingsExtensionMappings() { (client) => client.name === tenant.displayName, ) if (haloClient) { + console.log(haloClient) + console.log(tenant) return { tenant: tenant.customerId, haloClient: haloClient.label, From e0a5fcffa48484deba0bd224aa90f688d1375f2e Mon Sep 17 00:00:00 2001 From: John Duprey Date: Tue, 23 Apr 2024 16:55:09 -0400 Subject: [PATCH 049/475] Graph Explorer - Add Reverse Tenant lookups --- src/views/tenant/administration/GraphExplorer.jsx | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/views/tenant/administration/GraphExplorer.jsx b/src/views/tenant/administration/GraphExplorer.jsx index 42cfc7f0e2da..bad378c27d3a 100644 --- a/src/views/tenant/administration/GraphExplorer.jsx +++ b/src/views/tenant/administration/GraphExplorer.jsx @@ -500,6 +500,11 @@ const GraphExplorer = () => { placeholder="Select the number of rows to return" /> + + { placeholder="Enter OData search query" /> + + From 922eb3ad13f957f88681e1751c1c1696bf6af942 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Tue, 23 Apr 2024 23:14:07 +0200 Subject: [PATCH 050/475] case sensivity is the bane of my existence. --- src/views/cipp/app-settings/SettingsExtensionMappings.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/views/cipp/app-settings/SettingsExtensionMappings.jsx b/src/views/cipp/app-settings/SettingsExtensionMappings.jsx index 06410dc9fad2..a89e760d00ab 100644 --- a/src/views/cipp/app-settings/SettingsExtensionMappings.jsx +++ b/src/views/cipp/app-settings/SettingsExtensionMappings.jsx @@ -85,8 +85,8 @@ export function SettingsExtensionMappings() { console.log(haloClient) console.log(tenant) return { - tenant: tenant.customerId, - haloClient: haloClient.label, + Tenant: tenant.customerId, + haloName: haloClient.label, haloId: haloClient.value, } } From e30fe0a275c29d9b1df31233a5c1f7ccc0afb58f Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Tue, 23 Apr 2024 23:23:25 +0200 Subject: [PATCH 051/475] cleaned console logs --- src/components/tables/CippTable.jsx | 1 - src/components/tables/WizardTableField.jsx | 1 - src/store/api/baseQuery.js | 2 -- src/views/cipp/UserSettings.jsx | 11 ++++------- .../cipp/app-settings/SettingsExtensionMappings.jsx | 3 --- .../email-exchange/connectors/DeployConnector.jsx | 1 - .../email-exchange/spamfilter/DeploySpamfilter.jsx | 1 - .../email-exchange/transport/DeployTransport.jsx | 1 - .../endpoint/applications/ApplicationsAddWinGet.jsx | 2 -- src/views/endpoint/autopilot/AutopilotAddDevice.jsx | 1 - src/views/endpoint/intune/MEMAddPolicy.jsx | 1 - src/views/endpoint/intune/MEMListCompliance.jsx | 1 - src/views/identity/administration/AddUserBulk.jsx | 1 - .../identity/administration/DeployGroupTemplate.jsx | 1 - .../identity/administration/OffboardingWizard.jsx | 1 - src/views/tenant/administration/SecureScore.jsx | 1 - src/views/tenant/conditional/DeployCA.jsx | 1 - src/views/tenant/standards/ListAppliedStandards.jsx | 1 - 18 files changed, 4 insertions(+), 28 deletions(-) diff --git a/src/components/tables/CippTable.jsx b/src/components/tables/CippTable.jsx index dbcde884e90d..1651911c7be5 100644 --- a/src/components/tables/CippTable.jsx +++ b/src/components/tables/CippTable.jsx @@ -579,7 +579,6 @@ export default function CippTable({ } const executeselectedAction = (item) => { - // console.log(item) setModalContent({ item, }) diff --git a/src/components/tables/WizardTableField.jsx b/src/components/tables/WizardTableField.jsx index ac65a722029d..85f213d14d51 100644 --- a/src/components/tables/WizardTableField.jsx +++ b/src/components/tables/WizardTableField.jsx @@ -24,7 +24,6 @@ export default class WizardTableField extends React.Component { } handleSelect = ({ selectedRows = [] }) => { - // console.log(selectedRows) const { fieldProps, keyField } = this.props if (selectedRows.length > 0) { fieldProps.input.onChange(selectedRows) diff --git a/src/store/api/baseQuery.js b/src/store/api/baseQuery.js index 896401eccb83..90173f3b7cc2 100644 --- a/src/store/api/baseQuery.js +++ b/src/store/api/baseQuery.js @@ -19,8 +19,6 @@ export const axiosQuery = async ({ path, method = 'get', params, data, hideToast }) return { data: result.data } // Successful response } catch (error) { - console.log('error', error) - console.log('path', path) if (attempt === retryDelays.length || !shouldRetry(error, path)) { return { // Max retries reached or error should not trigger a retry diff --git a/src/views/cipp/UserSettings.jsx b/src/views/cipp/UserSettings.jsx index 8a4081b8c29b..cedc16b9e672 100644 --- a/src/views/cipp/UserSettings.jsx +++ b/src/views/cipp/UserSettings.jsx @@ -218,13 +218,10 @@ const UserSettings = () => { .reduce((acc, val) => acc.concat(val.items), []) //only map if 'name' property is not null .filter((item) => item?.name) - .map((item) => - // console.log(item), - ({ - name: item?.name, - value: { to: item?.to, name: item?.name }, - }), - )} + .map((item) => ({ + name: item?.name, + value: { to: item?.to, name: item?.name }, + }))} allowCreate={false} refreshFunction={() => setRandom3((Math.random() + 1).toString(36).substring(7)) diff --git a/src/views/cipp/app-settings/SettingsExtensionMappings.jsx b/src/views/cipp/app-settings/SettingsExtensionMappings.jsx index a89e760d00ab..4c0919beb0b6 100644 --- a/src/views/cipp/app-settings/SettingsExtensionMappings.jsx +++ b/src/views/cipp/app-settings/SettingsExtensionMappings.jsx @@ -74,7 +74,6 @@ export function SettingsExtensionMappings() { } const onHaloAutomap = (values) => { - console.log('starting automap') //check for a match between the tenant and the halo client based on the tenants displayName property, if so, create the mapping and add to the array. const newMappings = listBackendHaloResult.data?.Tenants.map( (tenant) => { @@ -82,8 +81,6 @@ export function SettingsExtensionMappings() { (client) => client.name === tenant.displayName, ) if (haloClient) { - console.log(haloClient) - console.log(tenant) return { Tenant: tenant.customerId, haloName: haloClient.label, diff --git a/src/views/email-exchange/connectors/DeployConnector.jsx b/src/views/email-exchange/connectors/DeployConnector.jsx index 16ebec88e10a..f2465f19d8ef 100644 --- a/src/views/email-exchange/connectors/DeployConnector.jsx +++ b/src/views/email-exchange/connectors/DeployConnector.jsx @@ -54,7 +54,6 @@ const DeployConnectorTemplate = () => { let template = EXConnectorTemplates.data.filter(function (obj) { return obj.GUID === value }) - // console.log(template[0][set]) onChange(JSON.stringify(template[0])) }} diff --git a/src/views/email-exchange/spamfilter/DeploySpamfilter.jsx b/src/views/email-exchange/spamfilter/DeploySpamfilter.jsx index 4a8c7e0e21b0..c45b21e3fb0d 100644 --- a/src/views/email-exchange/spamfilter/DeploySpamfilter.jsx +++ b/src/views/email-exchange/spamfilter/DeploySpamfilter.jsx @@ -54,7 +54,6 @@ const SpamFilterAdd = () => { let template = intuneTemplates.data.filter(function (obj) { return obj.GUID === value }) - // console.log(template[0][set]) onChange(JSON.stringify(template[0])) }} diff --git a/src/views/email-exchange/transport/DeployTransport.jsx b/src/views/email-exchange/transport/DeployTransport.jsx index 4ba34de996b4..8a0e5b6c35cb 100644 --- a/src/views/email-exchange/transport/DeployTransport.jsx +++ b/src/views/email-exchange/transport/DeployTransport.jsx @@ -54,7 +54,6 @@ const AddPolicy = () => { let template = TransportTemplates.data.filter(function (obj) { return obj.GUID === value }) - // console.log(template[0][set]) onChange(JSON.stringify(template[0])) }} diff --git a/src/views/endpoint/applications/ApplicationsAddWinGet.jsx b/src/views/endpoint/applications/ApplicationsAddWinGet.jsx index 525c39d93799..0880a67e3755 100644 --- a/src/views/endpoint/applications/ApplicationsAddWinGet.jsx +++ b/src/views/endpoint/applications/ApplicationsAddWinGet.jsx @@ -88,10 +88,8 @@ const AddWinGet = () => { {(value) => { let template = foundPackages.data.filter(function (obj) { - // console.log(value) return obj.packagename === value }) - //console.log(template[0]) onChange(template[0][set]) }} diff --git a/src/views/endpoint/autopilot/AutopilotAddDevice.jsx b/src/views/endpoint/autopilot/AutopilotAddDevice.jsx index 248f890b7e87..35abc7cc505f 100644 --- a/src/views/endpoint/autopilot/AutopilotAddDevice.jsx +++ b/src/views/endpoint/autopilot/AutopilotAddDevice.jsx @@ -93,7 +93,6 @@ const AddAPDevice = () => { } }) setAutopilotdata([...autopilotData, ...importdata]) - // console.log(importdata) } const handleOnError = (err, file, inputElem, reason) => { diff --git a/src/views/endpoint/intune/MEMAddPolicy.jsx b/src/views/endpoint/intune/MEMAddPolicy.jsx index 0b1de079d1e0..d742ba0c203a 100644 --- a/src/views/endpoint/intune/MEMAddPolicy.jsx +++ b/src/views/endpoint/intune/MEMAddPolicy.jsx @@ -215,7 +215,6 @@ const AddPolicy = () => { {(props) => { - console.log(props.values.RAWJson) const json = props.values?.RAWJson ? JSON.parse(props.values.RAWJson) : undefined return ( <> diff --git a/src/views/endpoint/intune/MEMListCompliance.jsx b/src/views/endpoint/intune/MEMListCompliance.jsx index c65a395997fd..6fbe0cdf84ce 100644 --- a/src/views/endpoint/intune/MEMListCompliance.jsx +++ b/src/views/endpoint/intune/MEMListCompliance.jsx @@ -19,7 +19,6 @@ import { cellBooleanFormatter, cellDateFormatter } from 'src/components/tables' const Actions = (row, rowIndex, formatExtraData) => { const [ocVisible, setOCVisible] = useState(false) - console.log(row) const tenant = useSelector((state) => state.app.currentTenant) return ( <> diff --git a/src/views/identity/administration/AddUserBulk.jsx b/src/views/identity/administration/AddUserBulk.jsx index 9b1e6dd3a507..bbf3805e5223 100644 --- a/src/views/identity/administration/AddUserBulk.jsx +++ b/src/views/identity/administration/AddUserBulk.jsx @@ -93,7 +93,6 @@ const AddUserBulk = () => { return item.data }) setBulkUser([...BulkUser, ...importdata]) - // console.log(importdata) } const handleOnError = (err, file, inputElem, reason) => { diff --git a/src/views/identity/administration/DeployGroupTemplate.jsx b/src/views/identity/administration/DeployGroupTemplate.jsx index f9bb9edfdb86..e86a70ff5228 100644 --- a/src/views/identity/administration/DeployGroupTemplate.jsx +++ b/src/views/identity/administration/DeployGroupTemplate.jsx @@ -60,7 +60,6 @@ const ApplyGroupTemplate = () => { let template = intuneTemplates.data.filter(function (obj) { return obj.GUID === value }) - // console.log(template[0][set]) onChange(template[0][set]) }} diff --git a/src/views/identity/administration/OffboardingWizard.jsx b/src/views/identity/administration/OffboardingWizard.jsx index 688be60b885f..e7ba79912958 100644 --- a/src/views/identity/administration/OffboardingWizard.jsx +++ b/src/views/identity/administration/OffboardingWizard.jsx @@ -133,7 +133,6 @@ const OffboardingWizard = () => { {/* eslint-disable react/prop-types */} {(props) => ( <> - {console.log(props.values)} {props.values.User?.length >= 3 && ( A maximum of three users is recommend. )} diff --git a/src/views/tenant/administration/SecureScore.jsx b/src/views/tenant/administration/SecureScore.jsx index 81ac84af7878..a99007b6a251 100644 --- a/src/views/tenant/administration/SecureScore.jsx +++ b/src/views/tenant/administration/SecureScore.jsx @@ -334,7 +334,6 @@ const SecureScore = () => { {info.title} - {console.log(info)} diff --git a/src/views/tenant/conditional/DeployCA.jsx b/src/views/tenant/conditional/DeployCA.jsx index 46de880a02da..b68a928391de 100644 --- a/src/views/tenant/conditional/DeployCA.jsx +++ b/src/views/tenant/conditional/DeployCA.jsx @@ -61,7 +61,6 @@ const AddPolicy = () => { let template = intuneTemplates.data.filter(function (obj) { return obj.GUID === value }) - // console.log(template[0][set]) onChange(JSON.stringify(template[0])) }} diff --git a/src/views/tenant/standards/ListAppliedStandards.jsx b/src/views/tenant/standards/ListAppliedStandards.jsx index 8a280ca6f830..3745c099aec7 100644 --- a/src/views/tenant/standards/ListAppliedStandards.jsx +++ b/src/views/tenant/standards/ListAppliedStandards.jsx @@ -69,7 +69,6 @@ const DeleteAction = () => { } const ApplyNewStandard = () => { const [templateStandard, setTemplateStandard] = useState() - console.log(templateStandard) const RefreshAction = () => { const [execStandards, execStandardsResults] = useLazyGenericGetRequestQuery() const { From c7729b50af1768cff344e2e45cfc996cbc6f220d Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Tue, 23 Apr 2024 23:29:24 +0200 Subject: [PATCH 052/475] Automapping callout --- .../cipp/app-settings/SettingsExtensionMappings.jsx | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/views/cipp/app-settings/SettingsExtensionMappings.jsx b/src/views/cipp/app-settings/SettingsExtensionMappings.jsx index 4c0919beb0b6..a1b5126c8255 100644 --- a/src/views/cipp/app-settings/SettingsExtensionMappings.jsx +++ b/src/views/cipp/app-settings/SettingsExtensionMappings.jsx @@ -21,7 +21,7 @@ export function SettingsExtensionMappings() { const [mappingValue, setMappingValue] = React.useState({}) const [haloMappingsArray, setHaloMappingsArray] = React.useState([]) const [ninjaMappingsArray, setNinjaMappingsArray] = React.useState([]) - + const [HaloAutoMap, setHaloAutoMap] = React.useState(false) const [listHaloBackend, listBackendHaloResult = []] = useLazyGenericGetRequestQuery() const [listNinjaOrgsBackend, listBackendNinjaOrgsResult] = useLazyGenericGetRequestQuery() const [listNinjaFieldsBackend, listBackendNinjaFieldsResult] = useLazyGenericGetRequestQuery() @@ -73,8 +73,8 @@ export function SettingsExtensionMappings() { }) } - const onHaloAutomap = (values) => { - //check for a match between the tenant and the halo client based on the tenants displayName property, if so, create the mapping and add to the array. + const onHaloAutomap = () => { + setHaloAutoMap(true) const newMappings = listBackendHaloResult.data?.Tenants.map( (tenant) => { const haloClient = listBackendHaloResult.data?.HaloClients.find( @@ -323,6 +323,12 @@ export function SettingsExtensionMappings() { + {HaloAutoMap && ( + + Automapping has been executed. Remember to check the changes and save + them. + + )} {(extensionHaloConfigResult.isSuccess || extensionHaloConfigResult.isError) && !extensionHaloConfigResult.isFetching && ( From 4d60d605e63d7611a9f04d7a849b4df4e21baa02 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Tue, 23 Apr 2024 17:37:56 -0400 Subject: [PATCH 053/475] Add partner relationships page --- src/_nav.jsx | 5 ++ src/importsMap.jsx | 1 + src/routes.json | 6 ++ .../administration/PartnerRelationships.jsx | 78 +++++++++++++++++++ 4 files changed, 90 insertions(+) create mode 100644 src/views/tenant/administration/PartnerRelationships.jsx diff --git a/src/_nav.jsx b/src/_nav.jsx index 26f7e0764890..da05474f3a34 100644 --- a/src/_nav.jsx +++ b/src/_nav.jsx @@ -162,6 +162,11 @@ const _nav = [ name: 'Tenant Offboarding', to: '/tenant/administration/tenant-offboarding-wizard', }, + { + component: CNavItem, + name: 'Partner Relationships', + to: '/tenant/administration/partner-relationships', + }, ], }, { diff --git a/src/importsMap.jsx b/src/importsMap.jsx index 0121566555bd..1ce828c16643 100644 --- a/src/importsMap.jsx +++ b/src/importsMap.jsx @@ -33,6 +33,7 @@ import React from 'react' "/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')), diff --git a/src/routes.json b/src/routes.json index 3d8eb476d3fb..7355cb3b9905 100644 --- a/src/routes.json +++ b/src/routes.json @@ -222,6 +222,12 @@ "component": "views/tenant/administration/EditTenant", "allowedRoles": ["admin", "editor", "readonly"] }, + { + "path": "/tenant/administration/partner-relationships", + "name": "Partner Relationships", + "component": "views/tenant/administration/PartnerRelationships", + "allowedRoles": ["admin", "editor", "readonly"] + }, { "path": "/tenant/administration/domains", "name": "Domains", diff --git a/src/views/tenant/administration/PartnerRelationships.jsx b/src/views/tenant/administration/PartnerRelationships.jsx new file mode 100644 index 000000000000..ea05fcb9e02b --- /dev/null +++ b/src/views/tenant/administration/PartnerRelationships.jsx @@ -0,0 +1,78 @@ +import React, { useEffect } from 'react' +import { useSelector } from 'react-redux' +import { CippPageList } from 'src/components/layout' +import { cellGenericFormatter } from 'src/components/tables/CellGenericFormat' + +const PartnerRelationships = () => { + const tenant = useSelector((state) => state.app.currentTenant) + const [tenantColumnSet, setTenantColumn] = React.useState(false) + useEffect(() => { + if (tenant.defaultDomainName === 'AllTenants') { + setTenantColumn(false) + } + if (tenant.defaultDomainName !== 'AllTenants') { + setTenantColumn(true) + } + }, [tenant.defaultDomainName, tenantColumnSet]) + + const columns = [ + { + name: 'Tenant', + selector: (row) => row.Tenant, + sortable: true, + exportSelector: 'Tenant', + omit: tenantColumnSet, + cell: cellGenericFormatter(), + }, + { + name: 'Partner', + selector: (row) => row.TenantInfo?.displayName, + sortable: true, + exportSelector: 'TenantInfo/displayName', + cell: cellGenericFormatter(), + }, + { + name: 'Service Provider', + selector: (row) => row['isServiceProvider'], + sortable: true, + exportSelector: 'isServiceProvider', + cell: cellGenericFormatter(), + }, + { + name: 'Multi Tenant', + selector: (row) => row['isInMultiTenantOrganization'], + sortable: true, + exportSelector: 'isInMultiTenantOrganization', + cell: cellGenericFormatter(), + }, + { + name: 'Partner Info', + selector: (row) => row['TenantInfo'], + sortable: true, + exportSelector: 'TenantInfo', + cell: cellGenericFormatter(), + }, + ] + return ( +
+ +
+ ) +} + +export default PartnerRelationships From 9cb2bbedf42153db3a5857f32a1e129a0a73bdda Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Tue, 23 Apr 2024 23:40:10 +0200 Subject: [PATCH 054/475] moved timing of showing success box. --- src/views/cipp/app-settings/SettingsExtensionMappings.jsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/views/cipp/app-settings/SettingsExtensionMappings.jsx b/src/views/cipp/app-settings/SettingsExtensionMappings.jsx index a1b5126c8255..c8a275dc14e5 100644 --- a/src/views/cipp/app-settings/SettingsExtensionMappings.jsx +++ b/src/views/cipp/app-settings/SettingsExtensionMappings.jsx @@ -74,16 +74,17 @@ export function SettingsExtensionMappings() { } const onHaloAutomap = () => { - setHaloAutoMap(true) const newMappings = listBackendHaloResult.data?.Tenants.map( (tenant) => { const haloClient = listBackendHaloResult.data?.HaloClients.find( (client) => client.name === tenant.displayName, ) if (haloClient) { + console.log(haloClient) + console.log(tenant) return { Tenant: tenant.customerId, - haloName: haloClient.label, + haloName: haloClient.name, haloId: haloClient.value, } } @@ -91,6 +92,8 @@ export function SettingsExtensionMappings() { //filter out any undefined values ).filter((item) => item !== undefined) setHaloMappingsArray((currentHaloMappings) => [...currentHaloMappings, ...newMappings]) + + setHaloAutoMap(true) } useEffect(() => { From f7ea986817e2805cc044fcdb213602e6d10caf2f Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Tue, 23 Apr 2024 23:48:10 +0200 Subject: [PATCH 055/475] whoops --- src/views/cipp/app-settings/SettingsExtensionMappings.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/cipp/app-settings/SettingsExtensionMappings.jsx b/src/views/cipp/app-settings/SettingsExtensionMappings.jsx index c8a275dc14e5..4676f197a217 100644 --- a/src/views/cipp/app-settings/SettingsExtensionMappings.jsx +++ b/src/views/cipp/app-settings/SettingsExtensionMappings.jsx @@ -83,7 +83,7 @@ export function SettingsExtensionMappings() { console.log(haloClient) console.log(tenant) return { - Tenant: tenant.customerId, + Tenant: tenant, haloName: haloClient.name, haloId: haloClient.value, } From d3ad2f80d76d2318ec35b4dfa12d065f2b6632a7 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Tue, 23 Apr 2024 21:41:21 -0400 Subject: [PATCH 056/475] Add API response offcanvas Tooltips for buttons --- src/components/tables/CippTable.jsx | 48 +++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 12 deletions(-) diff --git a/src/components/tables/CippTable.jsx b/src/components/tables/CippTable.jsx index dbcde884e90d..b3d38141b17e 100644 --- a/src/components/tables/CippTable.jsx +++ b/src/components/tables/CippTable.jsx @@ -16,6 +16,7 @@ import { CAccordionHeader, CAccordionBody, CAccordionItem, + CTooltip, } from '@coreui/react' import DataTable, { createTheme } from 'react-data-table-component' import PropTypes from 'prop-types' @@ -31,13 +32,12 @@ import { faSync, } from '@fortawesome/free-solid-svg-icons' import { cellGenericFormatter } from './CellGenericFormat' -import { ModalService } from '../utilities' +import { CippCodeOffCanvas, ModalService } from '../utilities' import { useLazyGenericGetRequestQuery, useLazyGenericPostRequestQuery } from 'src/store/api/app' 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 M365Licenses from 'src/data/M365Licenses' const FilterComponent = ({ filterText, onFilter, onClear, filterlist, onFilterPreset }) => ( <> @@ -155,6 +155,7 @@ export default function CippTable({ const [filterviaURL, setFilterviaURL] = React.useState(false) const [originalColumns, setOrginalColumns] = React.useState(columns) const [updatedColumns, setUpdatedColumns] = React.useState(columns) + const [codeOffcanvasVisible, setCodeOffcanvasVisible] = useState(false) if (defaultColumns && defaultColumnsSet === false && endpointName) { const defaultColumnsArray = defaultColumns.split(',').filter((item) => item) @@ -605,16 +606,18 @@ export default function CippTable({ } if (refreshFunction) { defaultActions.push([ - { - refreshFunction((Math.random() + 1).toString(36).substring(7)) - }} - className="m-1" - size="sm" - > - - , + + { + refreshFunction((Math.random() + 1).toString(36).substring(7)) + }} + className="m-1" + size="sm" + > + + + , ]) } @@ -815,6 +818,20 @@ export default function CippTable({ , ]) } + defaultActions.push([ + + { + setCodeOffcanvasVisible(true) + }} + className="m-1" + size="sm" + > + + + , + ]) return ( <>
@@ -982,6 +999,13 @@ export default function CippTable({ {...rest} /> {selectedRows.length >= 1 && Selected {selectedRows.length} items} + setCodeOffcanvasVisible(false)} + title="API Response" + /> )}
From f305105c52376a6dc9b6fef52515071cfc48968d Mon Sep 17 00:00:00 2001 From: John Duprey Date: Wed, 24 Apr 2024 15:44:08 -0400 Subject: [PATCH 057/475] Recent job details --- src/components/layout/AppHeader.jsx | 1 + .../utilities/CippActionsOffcanvas.jsx | 96 +++++++++++++------ src/components/utilities/CippCodeBlock.jsx | 37 ++++--- .../utilities/CippTableOffcanvas.jsx | 42 +++++++- 4 files changed, 130 insertions(+), 46 deletions(-) diff --git a/src/components/layout/AppHeader.jsx b/src/components/layout/AppHeader.jsx index 9661c89e7eba..620a2c9332c7 100644 --- a/src/components/layout/AppHeader.jsx +++ b/src/components/layout/AppHeader.jsx @@ -117,6 +117,7 @@ const AppHeader = () => { timestamp: job.Timestamp, percent: job.PercentComplete, progressText: `${job.PercentComplete}%`, + detailsObject: job.Tasks, })), ) } else { diff --git a/src/components/utilities/CippActionsOffcanvas.jsx b/src/components/utilities/CippActionsOffcanvas.jsx index f67e3d11ed88..7a78192aba10 100644 --- a/src/components/utilities/CippActionsOffcanvas.jsx +++ b/src/components/utilities/CippActionsOffcanvas.jsx @@ -21,7 +21,12 @@ import { CRow, CSpinner, } from '@coreui/react' -import { CippCodeBlock, CippOffcanvas, ModalService } from 'src/components/utilities' +import { + CippCodeBlock, + CippOffcanvas, + CippTableOffcanvas, + ModalService, +} from 'src/components/utilities' import { CippOffcanvasPropTypes } from 'src/components/utilities/CippOffcanvas' import { CippOffcanvasTable } from 'src/components/tables' import { useLazyGenericGetRequestQuery, useLazyGenericPostRequestQuery } from 'src/store/api/app' @@ -31,6 +36,65 @@ import ReactTimeAgo from 'react-time-ago' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { faGlobe } from '@fortawesome/free-solid-svg-icons' +const CippOffcanvasCard = ({ action, key }) => { + const [offcanvasVisible, setOffcanvasVisible] = useState(false) + return ( + <> + + + Report Name: {action.label} + + + + {action.value && ( + <> + {action?.link ? ( + Status: {action.value} + ) : ( + Status: {action.value} + )} + + )} + + {Array.isArray(action?.detailsObject) && ( + setOffcanvasVisible(true)}> + Details + + )} + {Array.isArray(action?.detailsObject) && ( + setOffcanvasVisible(false)} + /> + )} + + + + {action?.percent > 0 && ( + +
+ + {action?.progressText} + +
+
+ )} + + {action.timestamp && } + +
+
+
+ + ) +} +CippOffcanvasCard.propTypes = { + action: PropTypes.object, + key: PropTypes.object, +} + export default function CippActionsOffcanvas(props) { const inputRef = useRef('') const [genericGetRequest, getResults] = useLazyGenericGetRequestQuery() @@ -219,34 +283,7 @@ export default function CippActionsOffcanvas(props) { let cardContent try { cardContent = props.cards.map((action, index) => ( - <> - - - Report Name: {action.label} - - - - {action.value && Status: {action.value}} - - - - - {action?.percent > 0 && ( - -
- - {action?.progressText} - -
-
- )} - - {action.timestamp && } - -
-
-
- + )) } catch (error) { // swallow error @@ -343,6 +380,7 @@ export default function CippActionsOffcanvas(props) { diff --git a/src/components/utilities/CippCodeBlock.jsx b/src/components/utilities/CippCodeBlock.jsx index 00ccd3c54f7c..dbfc80d210e4 100644 --- a/src/components/utilities/CippCodeBlock.jsx +++ b/src/components/utilities/CippCodeBlock.jsx @@ -1,11 +1,12 @@ import React, { useState } from 'react' import PropTypes from 'prop-types' import { CopyToClipboard } from 'react-copy-to-clipboard' -import { CButton, CCallout } from '@coreui/react' +import { CButton } from '@coreui/react' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { faCopy, faClipboard } from '@fortawesome/free-regular-svg-icons' import SyntaxHighlighter from 'react-syntax-highlighter' import { atomOneDark } from 'react-syntax-highlighter/dist/esm/styles/hljs' +import { CippCallout } from '../layout' function CippCodeBlock({ code, @@ -16,7 +17,7 @@ function CippCodeBlock({ callout = false, calloutColour = 'info', calloutCopyValue = false, - dismissable = false, + calloutDismissible = false, }) { const [codeCopied, setCodeCopied] = useState(false) @@ -27,20 +28,26 @@ function CippCodeBlock({ return (
- onCodeCopied()}> - - {codeCopied ? : } - - + {!calloutDismissible && ( + onCodeCopied()}> + + {codeCopied ? ( + + ) : ( + + )} + + + )} {callout && ( - + {code} - + )} {!callout && ( { + return { + Key: key, + Value: data[key], + } + }) + } else { + if (Array.isArray(data) && typeof data[0] !== 'object') { + data = data.map((row) => { + return { + Value: row, + } + }) + } + } + columns = [] + Object.keys(data[0]).map((key) => { + columns.push({ + name: key, + selector: (row) => row[key], + sortable: true, + exportSelector: key, + cell: cellGenericFormatter(), + }) + }) + } + return ( <> - + <> + {data !== null && data !== undefined ? ( + + ) : ( + + )} + ) @@ -35,6 +72,7 @@ CippTableOffcanvas.propTypes = { params: PropTypes.object, columns: PropTypes.object, tableProps: PropTypes.object, + data: PropTypes.object, } export default CippTableOffcanvas From fbf2158ac1639c22532dfcbce1a8b17af25f3912 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Wed, 24 Apr 2024 22:52:33 +0200 Subject: [PATCH 058/475] improved standards templates --- .../tenant/standards/ListAppliedStandards.jsx | 148 ++++++++++-------- 1 file changed, 86 insertions(+), 62 deletions(-) diff --git a/src/views/tenant/standards/ListAppliedStandards.jsx b/src/views/tenant/standards/ListAppliedStandards.jsx index 3745c099aec7..33bb5b165403 100644 --- a/src/views/tenant/standards/ListAppliedStandards.jsx +++ b/src/views/tenant/standards/ListAppliedStandards.jsx @@ -13,6 +13,7 @@ import { CWidgetStatsB, CBadge, CFormInput, + CTooltip, } from '@coreui/react' import { Form, FormSpy } from 'react-final-form' import { @@ -29,7 +30,7 @@ import { useLazyGenericPostRequestQuery, } from 'src/store/api/app' import { faCheck, faCircleNotch, faExclamationTriangle } from '@fortawesome/free-solid-svg-icons' -import { CippContentCard, CippPage } from 'src/components/layout' +import { CippCallout, CippContentCard, CippPage } from 'src/components/layout' import { useSelector } from 'react-redux' import { ModalService, validateAlphabeticalSort } from 'src/components/utilities' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' @@ -39,6 +40,7 @@ import allStandardsList from 'src/data/standards' import CippCodeOffCanvas from 'src/components/utilities/CippCodeOffcanvas' import GDAPRoles from 'src/data/GDAPRoles' import Select from 'react-select' +import { cellGenericFormatter } from 'src/components/tables/CellGenericFormat' const DeleteAction = () => { const tenantDomain = useSelector((state) => state.app.currentTenant.defaultDomainName) @@ -91,32 +93,78 @@ const ApplyNewStandard = () => { onConfirm: () => execStandards({ path: `api/ExecStandardsRun?Tenantfilter=${selectedTenant}` }), }) - const ourRef = useRef() - const TemplateModal = () => + + const Offcanvas = (row, rowIndex, formatExtraData) => { + const handleDeleteIntuneTemplate = (apiurl, message) => { + ModalService.confirm({ + title: 'Confirm', + body:
{message}
, + onConfirm: () => ExecuteGetRequest({ path: apiurl }).then(() => refetchStandards()), + confirmLabel: 'Continue', + cancelLabel: 'Cancel', + }) + } + return ( + <> + + setTemplateStandard(row)} + > + + + + + handleDeleteIntuneTemplate( + `/api/RemoveStandardTemplate?ID=${row.GUID}`, + 'Do you want to delete the template?', + ) + } + > + + + + ) + } + const TemplateModal = () => { + const columns = [ + { + name: 'name', + selector: (row) => row['name'], + sortable: true, + exportSelector: 'name', + cell: cellGenericFormatter(), + }, + { + name: 'GUID', + selector: (row) => row['GUID'], + sortable: true, + exportSelector: 'GUID', + omit: true, + }, + { + name: 'Actions', + cell: Offcanvas, + maxWidth: '80px', + }, + ] + ModalService.open({ - body: ( -
- {isFetching && } - {isError && 'Something went wrong loading your templates'} - {isSuccess && ( - - - - - ) - }} - /> - - + +

{integration.helpText}

+ { + return ( + + + + {integration.SettingOptions.map( + (integrationOptions, idx) => + integrationOptions.type === 'input' && ( + + + + ), + )} + {integration.SettingOptions.map( + (integrationOptions, idx) => + integrationOptions.type === 'checkbox' && ( + + + + ), + )} + + + + + ) + }} + /> +
))} - +
) From 8f8b37691aff7ae722ba4139d40b26d534284c86 Mon Sep 17 00:00:00 2001 From: Chris Hamm <101881895+PremierOneData@users.noreply.github.com> Date: Mon, 13 May 2024 15:06:58 -0500 Subject: [PATCH 183/475] Update ConnectorList.jsx Added a variety of filters for Transport Connectors --- src/views/email-exchange/connectors/ConnectorList.jsx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/views/email-exchange/connectors/ConnectorList.jsx b/src/views/email-exchange/connectors/ConnectorList.jsx index 990e1d555199..5abba8ca1167 100644 --- a/src/views/email-exchange/connectors/ConnectorList.jsx +++ b/src/views/email-exchange/connectors/ConnectorList.jsx @@ -151,6 +151,14 @@ 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 }, From 1ef999ff3bfc472c61c6ed7cea9f0b53cfc3550e Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Mon, 13 May 2024 22:08:16 +0200 Subject: [PATCH 184/475] fix linting issue --- src/views/email-exchange/administration/QuarantineList.jsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/views/email-exchange/administration/QuarantineList.jsx b/src/views/email-exchange/administration/QuarantineList.jsx index 35c66dacc21f..7453af66b1a7 100644 --- a/src/views/email-exchange/administration/QuarantineList.jsx +++ b/src/views/email-exchange/administration/QuarantineList.jsx @@ -161,9 +161,10 @@ const QuarantineList = () => { 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?', + modalMessage: + 'Are you sure you want to release these messages, and add the senders to the whitelist?', }, - ] + ], }, params: { TenantFilter: tenant?.defaultDomainName }, }} From ef95912d938f3ec254291beef99eaf4324551770 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Tue, 14 May 2024 11:16:01 -0400 Subject: [PATCH 185/475] Add additional template types Fixes #2414 --- src/views/endpoint/intune/MEMAddPolicyTemplate.jsx | 2 ++ 1 file changed, 2 insertions(+) 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} From 5029187dd415db281b2d19330f1573f48b073e6c Mon Sep 17 00:00:00 2001 From: John Duprey Date: Tue, 14 May 2024 11:33:15 -0400 Subject: [PATCH 186/475] fix linting issue --- src/views/email-exchange/connectors/ConnectorList.jsx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/views/email-exchange/connectors/ConnectorList.jsx b/src/views/email-exchange/connectors/ConnectorList.jsx index 5abba8ca1167..c18d5eeb0004 100644 --- a/src/views/email-exchange/connectors/ConnectorList.jsx +++ b/src/views/email-exchange/connectors/ConnectorList.jsx @@ -156,8 +156,14 @@ const ConnectorList = () => { { 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' }, + { + 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', From e2bf01ee88aecf8090e27455d623b2dec73b3536 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Tue, 14 May 2024 14:10:33 -0400 Subject: [PATCH 187/475] On prem sync warnings --- src/views/identity/administration/EditUser.jsx | 8 +++++++- .../administration/OffboardingWizard.jsx | 18 ++++++++++++++++-- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/src/views/identity/administration/EditUser.jsx b/src/views/identity/administration/EditUser.jsx index 3cfc21641997..2f90bedb20e8 100644 --- a/src/views/identity/administration/EditUser.jsx +++ b/src/views/identity/administration/EditUser.jsx @@ -167,6 +167,12 @@ const EditUser = () => { 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} + ))} From 7313277f51fbe2403a6cd0e2a80dbdbb4cda6873 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Tue, 14 May 2024 21:30:30 +0200 Subject: [PATCH 188/475] updated audit log template --- src/data/AuditLogTemplates.json | 36 +++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/src/data/AuditLogTemplates.json b/src/data/AuditLogTemplates.json index 723b7c39dd34..d4a274e4f754 100644 --- a/src/data/AuditLogTemplates.json +++ b/src/data/AuditLogTemplates.json @@ -169,12 +169,44 @@ { "value": "Add service principal.", "name": "A service principal has been created", - "template": [] + "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": [] + "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", From f0e0bfeb10a9dc82e895c10c92711f5122569090 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Kj=C3=A6rg=C3=A5rd?= Date: Tue, 14 May 2024 21:34:41 +0200 Subject: [PATCH 189/475] Add filter for non-Microsoft service principals in ListEnterpriseApps.jsx --- src/views/tenant/administration/ListEnterpriseApps.jsx | 5 +++++ 1 file changed, 5 insertions(+) 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, From 7f546cad3fa874401f8ef040845a88c098209365 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Tue, 14 May 2024 19:05:28 -0400 Subject: [PATCH 190/475] Tenant Onboarding v2 --- src/_nav.jsx | 2 +- src/components/tables/CippTable.jsx | 4 +- src/importsMap.jsx | 1 + src/routes.json | 6 + .../administration/TenantOnboarding.jsx | 153 ++++++++++++++ .../administration/TenantOnboardingWizard.jsx | 184 +---------------- .../onboarding/RelationshipOnboarding.jsx | 195 ++++++++++++++++++ 7 files changed, 370 insertions(+), 175 deletions(-) create mode 100644 src/views/tenant/administration/TenantOnboarding.jsx create mode 100644 src/views/tenant/administration/onboarding/RelationshipOnboarding.jsx diff --git a/src/_nav.jsx b/src/_nav.jsx index 2f99dffd0953..902a8390b79f 100644 --- a/src/_nav.jsx +++ b/src/_nav.jsx @@ -150,7 +150,7 @@ const _nav = [ { component: CNavItem, name: 'Tenant Onboarding', - to: '/tenant/administration/tenant-onboarding-wizard', + to: '/tenant/administration/tenant-onboarding', }, { component: CNavItem, diff --git a/src/components/tables/CippTable.jsx b/src/components/tables/CippTable.jsx index c977c700cc87..f3ea486ee243 100644 --- a/src/components/tables/CippTable.jsx +++ b/src/components/tables/CippTable.jsx @@ -125,6 +125,7 @@ export default function CippTable({ filterlist, showFilter = true, endpointName, + defaultSortAsc = true, tableProps: { keyField = 'id', theme = 'cyberdrain', @@ -989,7 +990,7 @@ export default function CippTable({ expandableRowsComponent={expandableRowsComponent} highlightOnHover={highlightOnHover} expandOnRowClicked={expandOnRowClicked} - defaultSortAsc + defaultSortAsc={defaultSortAsc} defaultSortFieldId={1} sortFunction={customSort} paginationPerPage={tablePageSize} @@ -1050,6 +1051,7 @@ export const CippTablePropTypes = { disableCSVExport: PropTypes.bool, error: PropTypes.object, filterlist: PropTypes.arrayOf(PropTypes.object), + defaultSortAsc: PropTypes.bool, } CippTable.propTypes = CippTablePropTypes diff --git a/src/importsMap.jsx b/src/importsMap.jsx index 7b7589049b42..fd44014ae9c7 100644 --- a/src/importsMap.jsx +++ b/src/importsMap.jsx @@ -140,6 +140,7 @@ import React from 'react' "/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 \ No newline at end of file diff --git a/src/routes.json b/src/routes.json index e1d382459781..e681555e0346 100644 --- a/src/routes.json +++ b/src/routes.json @@ -954,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/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..874510c3517e 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,11 @@ const TenantOnboardingWizard = () => {
Tenant Onboarding Options

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

diff --git a/src/views/tenant/administration/onboarding/RelationshipOnboarding.jsx b/src/views/tenant/administration/onboarding/RelationshipOnboarding.jsx new file mode 100644 index 000000000000..3f5c93f4ab12 --- /dev/null +++ b/src/views/tenant/administration/onboarding/RelationshipOnboarding.jsx @@ -0,0 +1,195 @@ +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 }) => { + 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, + statusOnly: PropTypes.bool, +} + +export default RelationshipOnboarding From c8bcc83b06eaa9d11f60f74a18ff279f868935e5 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Wed, 15 May 2024 00:45:21 -0400 Subject: [PATCH 191/475] Onboarding Standards Exclusion --- .../utilities/CippTableOffcanvas.jsx | 2 +- .../administration/TenantOnboardingWizard.jsx | 10 +++++++ .../onboarding/RelationshipOnboarding.jsx | 29 +++++++++++++++---- 3 files changed, 34 insertions(+), 7 deletions(-) diff --git a/src/components/utilities/CippTableOffcanvas.jsx b/src/components/utilities/CippTableOffcanvas.jsx index 561d0e6839b1..236ccff04b4e 100644 --- a/src/components/utilities/CippTableOffcanvas.jsx +++ b/src/components/utilities/CippTableOffcanvas.jsx @@ -14,7 +14,7 @@ function CippTableOffcanvas({ tableProps, data = null, }) { - if (Array.isArray(data) && 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 { diff --git a/src/views/tenant/administration/TenantOnboardingWizard.jsx b/src/views/tenant/administration/TenantOnboardingWizard.jsx index 874510c3517e..ebbab2a8e178 100644 --- a/src/views/tenant/administration/TenantOnboardingWizard.jsx +++ b/src/views/tenant/administration/TenantOnboardingWizard.jsx @@ -183,6 +183,15 @@ const TenantOnboardingWizard = () => {
Tenant Onboarding Options

+
Standards
+
Optional Settings

Use these options for relationships created outside of the CIPP Invite Wizard or if the @@ -257,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 index 3f5c93f4ab12..9147caebbf63 100644 --- a/src/views/tenant/administration/onboarding/RelationshipOnboarding.jsx +++ b/src/views/tenant/administration/onboarding/RelationshipOnboarding.jsx @@ -40,11 +40,14 @@ function useInterval(callback, delay, state) { }, [delay, state]) } -const RelationshipOnboarding = ({ relationship, gdapRoles, autoMapRoles, addMissingGroups }) => { - const [relationshipReady, setRelationshipReady] = useState(false) - const [refreshGuid, setRefreshGuid] = useState(false) +const RelationshipOnboarding = ({ + relationship, + gdapRoles, + autoMapRoles, + addMissingGroups, + standardsExcludeAllTenants, +}) => { const [getOnboardingStatus, onboardingStatus] = useLazyGenericPostRequestQuery() - var headerIcon = relationshipReady ? 'check-circle' : 'question-circle' useInterval( async () => { @@ -58,6 +61,7 @@ const RelationshipOnboarding = ({ relationship, gdapRoles, autoMapRoles, addMiss 5000, onboardingStatus.data, ) + console.log(standardsExcludeAllTenants) return ( @@ -125,7 +129,13 @@ const RelationshipOnboarding = ({ relationship, gdapRoles, autoMapRoles, addMiss {onboardingStatus.isUninitialized && getOnboardingStatus({ path: '/api/ExecOnboardTenant', - values: { id: relationship.id, gdapRoles, autoMapRoles, addMissingGroups }, + values: { + id: relationship.id, + gdapRoles, + autoMapRoles, + addMissingGroups, + standardsExcludeAllTenants, + }, })} {onboardingStatus.isSuccess && ( <> @@ -134,7 +144,13 @@ const RelationshipOnboarding = ({ relationship, gdapRoles, autoMapRoles, addMiss onClick={() => getOnboardingStatus({ path: '/api/ExecOnboardTenant?Retry=True', - values: { id: relationship.id, gdapRoles, autoMapRoles, addMissingGroups }, + values: { + id: relationship.id, + gdapRoles, + autoMapRoles, + addMissingGroups, + standardsExcludeAllTenants, + }, }) } className="mb-3 me-2" @@ -190,6 +206,7 @@ RelationshipOnboarding.propTypes = { autoMapRoles: PropTypes.bool, addMissingGroups: PropTypes.bool, statusOnly: PropTypes.bool, + standardsExcludeAllTenants: PropTypes.bool, } export default RelationshipOnboarding From d23081336a5030ede339889376fe899fa297ee3f Mon Sep 17 00:00:00 2001 From: John Duprey Date: Wed, 15 May 2024 09:29:22 -0400 Subject: [PATCH 192/475] Add standard exclusion to partner webhook --- src/views/cipp/app-settings/SettingsPartner.jsx | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) 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." /> + Date: Wed, 15 May 2024 18:47:26 +0200 Subject: [PATCH 193/475] add member to role update --- src/data/AuditLogTemplates.json | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/data/AuditLogTemplates.json b/src/data/AuditLogTemplates.json index d4a274e4f754..957819ef53a0 100644 --- a/src/data/AuditLogTemplates.json +++ b/src/data/AuditLogTemplates.json @@ -87,7 +87,23 @@ { "value": "Add member to role.", "name": "A user has been added to an admin role", - "template": [] + "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.", From a9effcdd8eba1d7f1d1379b1a5903161cf412273 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Wed, 15 May 2024 18:52:58 +0200 Subject: [PATCH 194/475] strong authentication --- src/data/AuditLogSchema.json | 3 +++ src/data/AuditLogTemplates.json | 36 +++++++++++++++++++++++++++++++-- 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/src/data/AuditLogSchema.json b/src/data/AuditLogSchema.json index 1051d7b758cc..d29cdb5cffea 100644 --- a/src/data/AuditLogSchema.json +++ b/src/data/AuditLogSchema.json @@ -100,6 +100,9 @@ { "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" diff --git a/src/data/AuditLogTemplates.json b/src/data/AuditLogTemplates.json index 957819ef53a0..3b90b5eaedba 100644 --- a/src/data/AuditLogTemplates.json +++ b/src/data/AuditLogTemplates.json @@ -129,12 +129,44 @@ { "value": "Disable Strong Authentication.", "name": "A users MFA has been disabled", - "template": [] + "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": [] + "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.", From fcb2b70838f2890cab0b470658f09b2f8cc59bdb Mon Sep 17 00:00:00 2001 From: John Duprey Date: Wed, 15 May 2024 13:56:41 -0400 Subject: [PATCH 195/475] Dashboard - Clickable user chart --- src/views/home/Home.jsx | 67 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 60 insertions(+), 7 deletions(-) diff --git a/src/views/home/Home.jsx b/src/views/home/Home.jsx index f764cfc32915..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 CippPrettyCard from 'src/components/contentcards/CippPrettyCard' +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 ( <> @@ -337,6 +383,13 @@ const TenantDashboard = () => { labels: { color: getStyle('--cui-body-color'), }, + onClick: userChartLegendClickHandler, + onHover: (event) => { + event.native.target.style.cursor = 'pointer' + }, + onLeave: (event) => { + event.native.target.style.cursor = 'default' + }, }, }, }} From 7a7a8ca1ac06c236eaec69308b5e11f0da4b4e11 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Wed, 15 May 2024 20:18:40 +0200 Subject: [PATCH 196/475] version up --- package.json | 2 +- public/version_latest.txt | 2 +- version_latest.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 616ab46b1e64..332ac7504f6e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cipp", - "version": "5.6.2", + "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": { diff --git a/public/version_latest.txt b/public/version_latest.txt index d6a86bf436c0..42cdd0b540f9 100644 --- a/public/version_latest.txt +++ b/public/version_latest.txt @@ -1 +1 @@ -5.6.2 +5.7.0 diff --git a/version_latest.txt b/version_latest.txt index d6a86bf436c0..42cdd0b540f9 100644 --- a/version_latest.txt +++ b/version_latest.txt @@ -1 +1 @@ -5.6.2 +5.7.0 From eef83c80d49f51607badbbcb1941d0f40ab8b61f Mon Sep 17 00:00:00 2001 From: John Duprey Date: Fri, 17 May 2024 13:23:19 -0400 Subject: [PATCH 197/475] Prevent mapping with API errors --- src/views/cipp/app-settings/SettingsExtensionMappings.jsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/views/cipp/app-settings/SettingsExtensionMappings.jsx b/src/views/cipp/app-settings/SettingsExtensionMappings.jsx index de3246422499..33d7491795bf 100644 --- a/src/views/cipp/app-settings/SettingsExtensionMappings.jsx +++ b/src/views/cipp/app-settings/SettingsExtensionMappings.jsx @@ -337,6 +337,7 @@ export function SettingsExtensionMappings() { onClick={() => { if ( mappingValue.value !== undefined && + mappingValue.value !== '-1' && Object.values(haloMappingsArray) .map((item) => item.haloId) .includes(mappingValue.value) === false @@ -481,6 +482,7 @@ export function SettingsExtensionMappings() { //set the new mapping in the array if ( mappingValue.value !== undefined && + mappingValue.value !== '-1' && Object.values(ninjaMappingsArray) .map((item) => item.ninjaId) .includes(mappingValue.value) === false From b217bcf973fa58826ac0ff40d1d0ca06c24af6a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Kj=C3=A6rg=C3=A5rd?= Date: Fri, 17 May 2024 21:58:54 +0200 Subject: [PATCH 198/475] Update Activity Based Timeout standard to have options --- src/data/standards.json | 38 +++++++++++++++++++++++++++++++++----- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/src/data/standards.json b/src/data/standards.json index 92252d5a36ad..c9326b8b5f1f 100644 --- a/src/data/standards.json +++ b/src/data/standards.json @@ -102,9 +102,9 @@ "label": "Show header" }, { - "type":"boolean", - "name":"standards.Branding.isFooterShown", - "label":"Show footer" + "type": "boolean", + "name": "standards.Branding.isFooterShown", + "label": "Show footer" } ], "label": "Set branding for the tenant", @@ -156,8 +156,36 @@ "cat": "Global Standards", "tag": ["mediumimpact", "CIS", "spo_idle_session_timeout"], "helpText": "Enables and sets Idle session timeout for Microsoft 365 to 1 hour. This policy affects most M365 web apps", - "addedComponent": [], - "label": "Enable 1 hour Activity based Timeout", + "addedComponent": [ + { + "type": "Select", + "label": "Select value", + "name": "standards.ActivityBasedTimeout.timeout", + "values": [ + { + "label": "1 Hour", + "value": "01:00:00" + }, + { + "label": "3 Hours", + "value": "03:00:00" + }, + { + "label": "6 Hours", + "value": "06:00:00" + }, + { + "label": "12 Hours", + "value": "12:00:00" + }, + { + "label": "24 Hours", + "value": "1.00:00:00" + } + ] + } + ], + "label": "Enable Activity based Timeout", "impact": "Medium Impact", "impactColour": "warning" }, From 895ad36906dfc2e1ce615ad9a1a0b4432558005b Mon Sep 17 00:00:00 2001 From: Esco Date: Wed, 22 May 2024 13:22:10 +0200 Subject: [PATCH 199/475] Roles View Update --- src/views/identity/administration/Roles.jsx | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/views/identity/administration/Roles.jsx b/src/views/identity/administration/Roles.jsx index 79383c4f007d..600d5f31cd49 100644 --- a/src/views/identity/administration/Roles.jsx +++ b/src/views/identity/administration/Roles.jsx @@ -23,7 +23,7 @@ const Offcanvas = (row, rowIndex, formatExtraData) => { >

Role Group Name:
{row.DisplayName}



-
Member Names:
{row.Members ?

{row.Members}

:

Role has no members.

} +
Member Names:
{row.Members ? row.Members.split(',').map((member, index) =>

{member}

) :

Role has no members.

} ) @@ -53,6 +53,25 @@ const columns = [ exportSelector: 'Members', omit: true, }, + { + selector: (row) => row['Members'], + name: 'Assignments', + sortable: false, + cell: (row) => { + if (row.Members === 'none') { + return null; + } + const memberCount = row.Members ? row.Members.split(',').length : 0; + const memberText = row.Members && row.Members !== 'none' ? `Member${memberCount === 1 ? '' : 's'}` : null; + return ( + <> + {memberCount} {memberText} + + ); + }, + exportSelector: 'Members', + maxWidth: '150px', + }, { selector: (row) => 'View Members', name: 'Members', From 7a366873447d2f7c329e78b1f4908b42e5221209 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Sun, 26 May 2024 15:52:41 +0200 Subject: [PATCH 200/475] added top 300 --- src/views/tenant/administration/ListGDAPRelationships.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/tenant/administration/ListGDAPRelationships.jsx b/src/views/tenant/administration/ListGDAPRelationships.jsx index 11ae56e89e33..25b332ffdcaf 100644 --- a/src/views/tenant/administration/ListGDAPRelationships.jsx +++ b/src/views/tenant/administration/ListGDAPRelationships.jsx @@ -251,7 +251,7 @@ const GDAPRelationships = () => { columns, reportName: `GDAP-Relationships`, path: '/api/ListGraphRequest', - params: { Endpoint: 'tenantRelationships/delegatedAdminRelationships' }, + params: { Endpoint: 'tenantRelationships/delegatedAdminRelationships', $top: 300 }, }} />
From cfd802e046b6a4888167c9fbe17854caf7666483 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Sun, 26 May 2024 16:55:57 +0200 Subject: [PATCH 201/475] fixes offboarding wizard speed --- .../administration/OffboardingWizard.jsx | 45 ++++++++++--------- src/views/identity/administration/Roles.jsx | 16 ++++--- 2 files changed, 36 insertions(+), 25 deletions(-) diff --git a/src/views/identity/administration/OffboardingWizard.jsx b/src/views/identity/administration/OffboardingWizard.jsx index b5b344969c7a..571eba706e8b 100644 --- a/src/views/identity/administration/OffboardingWizard.jsx +++ b/src/views/identity/administration/OffboardingWizard.jsx @@ -47,7 +47,18 @@ const OffboardingWizard = () => { data: users = [], isFetching: usersIsFetching, error: usersError, - } = useListUsersQuery({ tenantDomain }) + } = useGenericGetRequestQuery({ + path: `/api/ListGraphRequest`, + params: { + TenantFilter: tenantDomain, + Endpoint: 'users', + $select: + 'id,displayName,givenName,mail,mailNickname,proxyAddresses,usageLocation,userPrincipalName,userType,assignedLicenses,onPremisesSyncEnabled', + $count: true, + $orderby: 'displayName', + $top: 999, + }, + }) const { data: recipients = [], @@ -121,7 +132,7 @@ const OffboardingWizard = () => { ({ + values={users?.Results?.map((user) => ({ value: user.userPrincipalName, name: `${user.displayName} <${user.userPrincipalName}>`, }))} @@ -177,36 +188,30 @@ const OffboardingWizard = () => { x.mail) - .map((user) => ({ - value: user.mail, - name: `${user.displayName} <${user.mail}>`, - }))} + values={users.Results?.filter((x) => x.mail).map((user) => ({ + value: user.mail, + name: `${user.displayName} <${user.mail}>`, + }))} placeholder={!usersIsFetching ? 'Select user' : 'Loading...'} name="AccessNoAutomap" /> x.mail) - .map((user) => ({ - value: user.mail, - name: `${user.displayName} <${user.mail}>`, - }))} + values={users.Results?.filter((x) => x.mail).map((user) => ({ + value: user.mail, + name: `${user.displayName} <${user.mail}>`, + }))} placeholder={!usersIsFetching ? 'Select user' : 'Loading...'} name="AccessAutomap" /> x.mail) - .map((user) => ({ - value: user.mail, - name: `${user.displayName} <${user.mail}>`, - }))} + values={users.Results?.filter((x) => x.mail).map((user) => ({ + value: user.mail, + name: `${user.displayName} <${user.mail}>`, + }))} placeholder={!usersIsFetching ? 'Select user' : 'Loading...'} name="OnedriveAccess" /> diff --git a/src/views/identity/administration/Roles.jsx b/src/views/identity/administration/Roles.jsx index 600d5f31cd49..7e103a130920 100644 --- a/src/views/identity/administration/Roles.jsx +++ b/src/views/identity/administration/Roles.jsx @@ -23,7 +23,12 @@ const Offcanvas = (row, rowIndex, formatExtraData) => { >
Role Group Name:
{row.DisplayName}



-
Member Names:
{row.Members ? row.Members.split(',').map((member, index) =>

{member}

) :

Role has no members.

} +
Member Names:
{' '} + {row.Members ? ( + row.Members.split(',').map((member, index) =>

{member}

) + ) : ( +

Role has no members.

+ )} ) @@ -59,15 +64,16 @@ const columns = [ sortable: false, cell: (row) => { if (row.Members === 'none') { - return null; + return null } - const memberCount = row.Members ? row.Members.split(',').length : 0; - const memberText = row.Members && row.Members !== 'none' ? `Member${memberCount === 1 ? '' : 's'}` : null; + const memberCount = row.Members ? row.Members.split(',').length : 0 + const memberText = + row.Members && row.Members !== 'none' ? `Member${memberCount === 1 ? '' : 's'}` : null return ( <> {memberCount} {memberText} - ); + ) }, exportSelector: 'Members', maxWidth: '150px', From 202962fec5c052656197c6b4d0ac0f3e07f32117 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Sun, 26 May 2024 18:34:14 +0200 Subject: [PATCH 202/475] removed data from loop --- src/components/tables/CippTable.jsx | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/components/tables/CippTable.jsx b/src/components/tables/CippTable.jsx index f3ea486ee243..e858c5e28fc6 100644 --- a/src/components/tables/CippTable.jsx +++ b/src/components/tables/CippTable.jsx @@ -414,6 +414,7 @@ export default function CippTable({ (modalMessage, modalUrl, modalType = 'GET', modalBody, modalInput, modalDropdown) => { if (modalType === 'GET') { ModalService.confirm({ + getData: () => inputRef.current?.value, body: (
{modalMessage}
@@ -466,6 +467,18 @@ export default function CippTable({ title: 'Confirm', onConfirm: async () => { const resultsarr = [] + const selectedValue = inputRef.current.value + let additionalFields = {} + if (inputRef.current.nodeName === 'SELECT') { + const selectedItem = dropDownInfo.data.find( + (item) => item[modalDropdown.valueField] === selectedValue, + ) + if (selectedItem && modalDropdown.addedField) { + Object.keys(modalDropdown.addedField).forEach((key) => { + additionalFields[key] = selectedItem[modalDropdown.addedField[key]] + }) + } + } for (const row of selectedRows) { setLoopRunning(true) const urlParams = new URLSearchParams(modalUrl.split('?')[1]) @@ -492,26 +505,13 @@ export default function CippTable({ } } const NewModalUrl = `${modalUrl.split('?')[0]}?${urlParams.toString()}` - const selectedValue = inputRef.current.value - let additionalFields = {} - if (inputRef.current.nodeName === 'SELECT') { - const selectedItem = dropDownInfo.data.find( - (item) => item[modalDropdown.valueField] === selectedValue, - ) - if (selectedItem && modalDropdown.addedField) { - Object.keys(modalDropdown.addedField).forEach((key) => { - additionalFields[key] = selectedItem[modalDropdown.addedField[key]] - }) - } - } - const results = await genericPostRequest({ path: NewModalUrl, values: { ...modalBody, ...newModalBody, ...additionalFields, - ...{ input: inputRef.current.value }, + ...{ input: selectedValue }, }, }) resultsarr.push(results) From fe06f78873324dcbc6ce7a2d02a1445f1a6c7439 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Sun, 26 May 2024 19:00:18 +0200 Subject: [PATCH 203/475] added bulk options for policies --- src/views/endpoint/intune/MEMListAppProtection.jsx | 11 ++++++++++- src/views/endpoint/intune/MEMListCompliance.jsx | 11 ++++++++++- src/views/endpoint/intune/MEMListPolicies.jsx | 9 +++++++++ 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/src/views/endpoint/intune/MEMListAppProtection.jsx b/src/views/endpoint/intune/MEMListAppProtection.jsx index 569608ec916c..f017ba2f2065 100644 --- a/src/views/endpoint/intune/MEMListAppProtection.jsx +++ b/src/views/endpoint/intune/MEMListAppProtection.jsx @@ -48,7 +48,7 @@ const Actions = (row, rowIndex, formatExtraData) => { color: 'danger', modal: true, icon: , - modalUrl: `/api/RemovePolicy?TenantFilter=${tenant.defaultDomainName}&ID=${row.id}&URLName=${row.URLName}`, + modalUrl: `/api/RemovePolicy?TenantFilter=${tenant.defaultDomainName}&ID=${row.id}&URLName=managedAppPolicies`, modalMessage: 'Are you sure you want to delete this policy?', }, ]} @@ -122,6 +122,15 @@ const AppProtectionList = () => { expandableRows: true, expandableRowsComponent: ExpandedComponent, expandOnRowClicked: true, + selectableRows: true, + actionsList: [ + { + label: 'Delete Policy', + modal: true, + modalUrl: `api/RemovePolicy?TenantFilter=${tenant?.defaultDomainName}&ID=!id&URLName=managedAppPolicies`, + modalMessage: 'Are you sure you want to convert these users to a shared mailbox?', + }, + ], }, }} /> diff --git a/src/views/endpoint/intune/MEMListCompliance.jsx b/src/views/endpoint/intune/MEMListCompliance.jsx index 6fbe0cdf84ce..9b970031bb1d 100644 --- a/src/views/endpoint/intune/MEMListCompliance.jsx +++ b/src/views/endpoint/intune/MEMListCompliance.jsx @@ -71,7 +71,7 @@ const Actions = (row, rowIndex, formatExtraData) => { color: 'danger', modal: true, icon: , - modalUrl: `/api/RemovePolicy?TenantFilter=${tenant.defaultDomainName}&ID=${row.id}&URLName=${row.URLName}`, + modalUrl: `/api/RemovePolicy?TenantFilter=${tenant.defaultDomainName}&ID=${row.id}&URLName=deviceCompliancePolicies`, modalMessage: 'Are you sure you want to delete this policy?', }, ]} @@ -145,6 +145,15 @@ const ComplianceList = () => { expandableRows: true, expandableRowsComponent: ExpandedComponent, expandOnRowClicked: true, + selectableRows: true, + actionsList: [ + { + label: 'Delete Policy', + modal: true, + modalUrl: `api/RemovePolicy?TenantFilter=${tenant?.defaultDomainName}&ID=!id&URLName=deviceCompliancePolicies`, + modalMessage: 'Are you sure you want to convert these users to a shared mailbox?', + }, + ], }, }} /> diff --git a/src/views/endpoint/intune/MEMListPolicies.jsx b/src/views/endpoint/intune/MEMListPolicies.jsx index 0ea3ed1a26f4..f9f82026cd52 100644 --- a/src/views/endpoint/intune/MEMListPolicies.jsx +++ b/src/views/endpoint/intune/MEMListPolicies.jsx @@ -140,6 +140,15 @@ const IntuneList = () => { expandableRows: true, expandableRowsComponent: ExpandedComponent, expandOnRowClicked: true, + selectableRows: true, + actionsList: [ + { + label: 'Delete Policy', + modal: true, + modalUrl: `api/RemovePolicy?TenantFilter=${tenant?.defaultDomainName}&ID=!id&URLName=!URLName`, + modalMessage: 'Are you sure you want to convert these users to a shared mailbox?', + }, + ], }, }} /> From a5505d3c0786665680df5cb3c90888f6166a23ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Kj=C3=A6rg=C3=A5rd?= Date: Sun, 26 May 2024 21:54:13 +0200 Subject: [PATCH 204/475] Add shorten meetings standard --- src/data/standards.json | 42 +++++++++++++++++++++++++++++++++++++++++ src/views/home/Home.jsx | 3 +-- 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/src/data/standards.json b/src/data/standards.json index c9326b8b5f1f..3efbc09ea197 100644 --- a/src/data/standards.json +++ b/src/data/standards.json @@ -830,6 +830,48 @@ "impact": "Low Impact", "impactColour": "info" }, + { + "name": "standards.ShortenMeetings", + "cat": "Exchange Standards", + "tag": ["mediumimpact"], + "helpText": "Sets the shorten meetings settings on a tenant level. This will shorten meetings by the selected amount of minutes. Valid values are 0 to 29. Short meetings are under 60 minutes, long meetings are over 60 minutes.", + "addedComponent": [ + { + "type": "Select", + "label": "Select value", + "name": "standards.ShortenMeetings.ShortenEventScopeDefault", + "values": [ + { + "label": "Disabled/None", + "value": "None" + }, + { + "label": "End early", + "value": "EndEarly" + }, + { + "label": "Start late", + "value": "StartLate" + } + ] + }, + { + "type": "number", + "name": "standards.ShortenMeetings.DefaultMinutesToReduceShortEventsBy", + "label": "Minutes to reduce short calendar events by (Default is 5)", + "default": 5 + }, + { + "type": "number", + "name": "standards.ShortenMeetings.DefaultMinutesToReduceLongEventsBy", + "label": "Minutes to reduce long calendar events by (Default is 10)", + "default": 10 + } + ], + "label": "Set shorten meetings state", + "impact": "Medium Impact", + "impactColour": "warning" + }, { "name": "standards.DisableOutlookAddins", "cat": "Exchange Standards", diff --git a/src/views/home/Home.jsx b/src/views/home/Home.jsx index 432bd10e0870..7111f9e41bb2 100644 --- a/src/views/home/Home.jsx +++ b/src/views/home/Home.jsx @@ -30,8 +30,7 @@ import CippCopyToClipboard from 'src/components/utilities/CippCopyToClipboard' import { CChart } from '@coreui/react-chartjs' import { getStyle } from '@coreui/utils' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' -import { Link } from 'react-router-dom' -import { useNavigate } from 'react-router-dom' +import { useNavigate, Link } from 'react-router-dom' import { cellGenericFormatter } from 'src/components/tables/CellGenericFormat' import { ModalService } from 'src/components/utilities' From 5059a0301d63b011b405595781e84d37e15ee2e4 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Mon, 27 May 2024 16:05:13 -0400 Subject: [PATCH 205/475] Graph Explorer Scheduler Add CippScheduleOffcanvas --- .../utilities/CippScheduleOffcanvas.jsx | 273 ++++++++++++++++++ src/views/cipp/Scheduler.jsx | 1 - .../tenant/administration/GraphExplorer.jsx | 53 ++++ 3 files changed, 326 insertions(+), 1 deletion(-) create mode 100644 src/components/utilities/CippScheduleOffcanvas.jsx diff --git a/src/components/utilities/CippScheduleOffcanvas.jsx b/src/components/utilities/CippScheduleOffcanvas.jsx new file mode 100644 index 000000000000..b040ff031192 --- /dev/null +++ b/src/components/utilities/CippScheduleOffcanvas.jsx @@ -0,0 +1,273 @@ +import React, { useState } from 'react' +import PropTypes from 'prop-types' +import { + CButton, + CCallout, + CCard, + CCardBody, + CCardHeader, + CCol, + CForm, + CRow, + CSpinner, + CTooltip, +} from '@coreui/react' +import { CippOffcanvas, TenantSelector } from '.' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { Field, Form, FormSpy } from 'react-final-form' +import arrayMutators from 'final-form-arrays' +import { + RFFCFormInput, + RFFCFormInputArray, + RFFCFormSwitch, + RFFSelectSearch, +} from 'src/components/forms' +import { useSelector } from 'react-redux' +import { useGenericGetRequestQuery, useLazyGenericPostRequestQuery } from 'src/store/api/app' +import DatePicker from 'react-datepicker' +import 'react-datepicker/dist/react-datepicker.css' + +export default function CippScheduleOffcanvas({ + state: visible, + hideFunction, + title, + placement, + ...props +}) { + const currentDate = new Date() + const [startDate, setStartDate] = useState(currentDate) + const tenantDomain = useSelector((state) => state.app.currentTenant.defaultDomainName) + const [refreshState, setRefreshState] = useState(false) + const taskName = `Scheduled Task ${currentDate.toLocaleString()}` + const { data: availableCommands = [], isLoading: isLoadingcmd } = useGenericGetRequestQuery({ + path: 'api/ListFunctionParameters?Module=CIPPCore', + }) + + const recurrenceOptions = [ + { value: '0', name: 'Only once' }, + { value: '1', name: 'Every 1 day' }, + { value: '7', name: 'Every 7 days' }, + { value: '30', name: 'Every 30 days' }, + { value: '365', name: 'Every 365 days' }, + ] + + const [genericPostRequest, postResults] = useLazyGenericPostRequestQuery() + const onSubmit = (values) => { + const unixTime = Math.floor(startDate.getTime() / 1000) + const shippedValues = { + TenantFilter: tenantDomain, + Name: values.taskName, + Command: values.command, + Parameters: values.parameters, + ScheduledTime: unixTime, + Recurrence: values.Recurrence, + AdditionalProperties: values.additional, + PostExecution: { + Webhook: values.webhook, + Email: values.email, + PSA: values.psa, + }, + } + genericPostRequest({ path: '/api/AddScheduledItem', values: shippedValues }).then((res) => { + setRefreshState(res.requestId) + }) + } + + return ( + + + + + { + return ( + + + + + {(props) => } + + + + + + + + + + + setStartDate(date)} + /> + + + + + + + + + + ({ + value: cmd.Function, + name: cmd.Function, + }))} + name="command" + placeholder={ + isLoadingcmd ? ( + + ) : ( + 'Select a command or report to execute.' + ) + } + label="Command to execute" + /> + + + + {/* eslint-disable react/prop-types */} + {(props) => { + const selectedCommand = availableCommands.find( + (cmd) => cmd.Function === props.values.command?.value, + ) + return ( + + {selectedCommand?.Synopsis} + + ) + }} + + + + {/* eslint-disable react/prop-types */} + {(props) => { + const selectedCommand = availableCommands.find( + (cmd) => cmd.Function === props.values.command?.value, + ) + let paramblock = null + if (selectedCommand) { + //if the command parameter type is boolean we use else . + const parameters = selectedCommand.Parameters + if (parameters.length > 0) { + paramblock = parameters.map((param, idx) => ( + + + + {param.Type === 'System.Boolean' || + param.Type === + 'System.Management.Automation.SwitchParameter' ? ( + <> + + + + ) : ( + <> + {param.Type === 'System.Collections.Hashtable' ? ( + + ) : ( + + )} + + )} + + + + )) + } + } + return paramblock + }} + + + + + + + + + + + + + + + + + + + Add Schedule + {postResults.isFetching && ( + + )} + + + + {postResults.isSuccess && ( + +
  • {postResults.data.Results}
  • +
    + )} +
    + ) + }} + /> +
    +
    +
    + ) +} + +CippScheduleOffcanvas.propTypes = { + groups: PropTypes.array, + placement: PropTypes.string.isRequired, + title: PropTypes.string.isRequired, + state: PropTypes.bool, + hideFunction: PropTypes.func.isRequired, +} diff --git a/src/views/cipp/Scheduler.jsx b/src/views/cipp/Scheduler.jsx index aa07fb3722b6..7ffed5837fe0 100644 --- a/src/views/cipp/Scheduler.jsx +++ b/src/views/cipp/Scheduler.jsx @@ -157,7 +157,6 @@ const Scheduler = () => { if (typeof row?.Parameters[key] === 'object') { var nestedParamList = [] Object.keys(row?.Parameters[key]).forEach((nestedKey) => { - console.log(nestedKey) nestedParamList.push({ Key: nestedKey, Value: row?.Parameters[key][nestedKey], diff --git a/src/views/tenant/administration/GraphExplorer.jsx b/src/views/tenant/administration/GraphExplorer.jsx index 3d6950c598d4..ebc3abd8b39d 100644 --- a/src/views/tenant/administration/GraphExplorer.jsx +++ b/src/views/tenant/administration/GraphExplorer.jsx @@ -32,6 +32,7 @@ import { cellGenericFormatter } from 'src/components/tables/CellGenericFormat' import PropTypes from 'prop-types' import { CippCodeOffCanvas, ModalService } from 'src/components/utilities' import { debounce } from 'lodash-es' +import CippScheduleOffcanvas from 'src/components/utilities/CippScheduleOffcanvas' const GraphExplorer = () => { const tenant = useSelector((state) => state.app.currentTenant) @@ -57,6 +58,8 @@ const GraphExplorer = () => { error: presetsError, } = useGenericGetRequestQuery({ path: '/api/ListGraphExplorerPresets', params: { random2 } }) const QueryColumns = { set: false, data: [] } + const [scheduleVisible, setScheduleVisible] = useState(false) + const [scheduleValues, setScheduleValues] = useState({}) const debounceEndpointChange = useMemo(() => { function endpointChange(value) { @@ -148,6 +151,36 @@ const GraphExplorer = () => { }) } + function handleSchedule(values) { + var graphParameters = [] + const paramNames = ['$filter', '$format', '$search', '$select', '$top'] + paramNames.map((param) => { + if (values[param]) { + if (Array.isArray(values[param])) { + graphParameters.push({ Key: param, Value: values[param].map((p) => p.value).join(',') }) + } else { + graphParameters.push({ Key: param, Value: values[param] }) + } + } + }) + + const reportName = values.name ?? 'Graph Explorer' + const shippedValues = { + taskName: reportName + ' - ' + tenant.displayName, + command: { label: 'Get-GraphRequestList', value: 'Get-GraphRequestList' }, + parameters: { + Parameters: graphParameters, + NoPagination: values.NoPagination, + ReverseTenantLookup: values.ReverseTenantLookup, + ReverseTenantLookupProperty: values.ReverseTenantLookupProperty, + Endpoint: values.endpoint, + SkipCache: true, + }, + } + setScheduleValues(shippedValues) + setScheduleVisible(true) + } + const presets = [ { name: 'All users with email addresses', @@ -617,6 +650,19 @@ const GraphExplorer = () => { Query + + {(props) => { + return ( + handleSchedule(props.values)} + className="ms-2" + > + + Schedule Report + + ) + }} + @@ -628,6 +674,13 @@ const GraphExplorer = () => { + setScheduleVisible(false)} + initialValues={scheduleValues} + />
    {!searchNow && Execute a search to get started.} From b26aea1efb4a6f051046c5ef6aa47d9ec4dec9ed Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Tue, 28 May 2024 12:12:51 +0200 Subject: [PATCH 206/475] improvements to autopilot. --- .../endpoint/autopilot/AutopilotAddDevice.jsx | 36 +++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/src/views/endpoint/autopilot/AutopilotAddDevice.jsx b/src/views/endpoint/autopilot/AutopilotAddDevice.jsx index 35abc7cc505f..b475e2712388 100644 --- a/src/views/endpoint/autopilot/AutopilotAddDevice.jsx +++ b/src/views/endpoint/autopilot/AutopilotAddDevice.jsx @@ -34,6 +34,28 @@ Error.propTypes = { const AddAPDevice = () => { const [genericPostRequest, postResults] = useLazyGenericPostRequestQuery() const [autopilotData, setAutopilotdata] = useState([]) + const completeColumns = [ + { + name: 'Serial Number', + selector: (row) => row['serialNumber'], + sortable: true, + }, + { + name: 'Status', + selector: (row) => row['status'], + sortable: true, + }, + { + name: 'Error Code', + selector: (row) => row['errorCode'], + sortable: true, + }, + { + name: 'Error Description', + selector: (row) => row['errorDescription'], + sortable: true, + }, + ] const tableColumns = [ { name: 'serialNumber', @@ -267,8 +289,18 @@ const AddAPDevice = () => { Loading )} - {postResults.isSuccess && {postResults.data.Results}} - {autopilotData && ( + {postResults.isSuccess && ( + <> + {postResults.data?.Results?.Status} + + + )} + {autopilotData && !postResults.isSuccess && ( Date: Tue, 28 May 2024 18:05:03 +0200 Subject: [PATCH 207/475] pretification --- src/views/identity/administration/OffboardingWizard.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/identity/administration/OffboardingWizard.jsx b/src/views/identity/administration/OffboardingWizard.jsx index 571eba706e8b..4d6c496951f8 100644 --- a/src/views/identity/administration/OffboardingWizard.jsx +++ b/src/views/identity/administration/OffboardingWizard.jsx @@ -302,7 +302,7 @@ const OffboardingWizard = () => { >
    Selected User:
    - {users.find((x) => x.userPrincipalName === user.value) + {users.Results?.find((x) => x.userPrincipalName === user.value) .onPremisesSyncEnabled === true ? ( Date: Tue, 28 May 2024 13:57:57 -0500 Subject: [PATCH 208/475] Update Users.jsx Created filter for Non-guest (Member) users; Fixed "AAD users" filter to handle null values --- src/views/identity/administration/Users.jsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/views/identity/administration/Users.jsx b/src/views/identity/administration/Users.jsx index 2d0a4aa9ac49..0f34be2af7ef 100644 --- a/src/views/identity/administration/Users.jsx +++ b/src/views/identity/administration/Users.jsx @@ -447,11 +447,12 @@ const Users = (row) => { filterlist: [ { filterName: 'Enabled users', filter: '"accountEnabled":true' }, { filterName: 'Disabled users', filter: '"accountEnabled":false' }, - { filterName: 'AAD users', filter: '"onPremisesSyncEnabled":false' }, + { filterName: 'AAD users', filter: 'Complex: onPremisesSyncEnabled ne True' }, { filterName: 'Synced users', filter: '"onPremisesSyncEnabled":true', }, + { filterName: 'Non-guest users', filter: 'Complex: usertype ne Guest' }, { filterName: 'Guest users', filter: '"usertype":"guest"' }, { filterName: 'Users with a license', From 6fd3c252874e416978d754ccc13e58dd69ed2d99 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Tue, 28 May 2024 20:11:53 -0400 Subject: [PATCH 209/475] Custom Roles --- src/components/forms/RFFComponents.jsx | 2 - .../utilities/CippScheduleOffcanvas.jsx | 3 + .../cipp/app-settings/SettingsSuperAdmin.jsx | 130 +++---- .../components/SettingsCustomRoles.jsx | 351 ++++++++++++++++++ 4 files changed, 422 insertions(+), 64 deletions(-) create mode 100644 src/views/cipp/app-settings/components/SettingsCustomRoles.jsx diff --git a/src/components/forms/RFFComponents.jsx b/src/components/forms/RFFComponents.jsx index 737333c7dfc9..01b4ed8f083b 100644 --- a/src/components/forms/RFFComponents.jsx +++ b/src/components/forms/RFFComponents.jsx @@ -293,7 +293,6 @@ export const RFFCFormRadioList = ({ name, options, className = 'mb-3', - disabled = false, onClick, inline = false, }) => { @@ -312,7 +311,6 @@ export const RFFCFormRadioList = ({ onChange={input.onChange} type="radio" {...option} - disabled={disabled} onClick={onClick} inline={inline} /> diff --git a/src/components/utilities/CippScheduleOffcanvas.jsx b/src/components/utilities/CippScheduleOffcanvas.jsx index b040ff031192..45d5b5a62e92 100644 --- a/src/components/utilities/CippScheduleOffcanvas.jsx +++ b/src/components/utilities/CippScheduleOffcanvas.jsx @@ -70,6 +70,9 @@ export default function CippScheduleOffcanvas({ } genericPostRequest({ path: '/api/AddScheduledItem', values: shippedValues }).then((res) => { setRefreshState(res.requestId) + if (props.submitFunction) { + props.submitFunction() + } }) } diff --git a/src/views/cipp/app-settings/SettingsSuperAdmin.jsx b/src/views/cipp/app-settings/SettingsSuperAdmin.jsx index 7650089b5a23..bec964fa3386 100644 --- a/src/views/cipp/app-settings/SettingsSuperAdmin.jsx +++ b/src/views/cipp/app-settings/SettingsSuperAdmin.jsx @@ -1,10 +1,11 @@ import { useGenericGetRequestQuery, useLazyGenericPostRequestQuery } from 'src/store/api/app.js' -import { CButton, CCol, CForm, CLink, CRow, CSpinner } from '@coreui/react' +import { CAccordion, CButton, CCol, CForm, CLink, CRow, CSpinner } from '@coreui/react' import { Form } from 'react-final-form' import { RFFCFormRadio } from 'src/components/forms/index.js' import React 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 SettingsCustomRoles from 'src/views/cipp/app-settings/components/SettingsCustomRoles' export function SettingsSuperAdmin() { const partnerConfig = useGenericGetRequestQuery({ @@ -38,68 +39,73 @@ export function SettingsSuperAdmin() { ) return ( - - <> + + <> - - -

    - The configuration settings below should only be modified by a super admin. Super - admins can configure what tenant mode CIPP operates in. See - - our documentation - - for more information on how to configure these modes and what they mean. -

    -
    -
    - - -

    Tenant Mode

    - ( - <> - {partnerConfig.isFetching && } - - - - - - + <> + + +

    + The configuration settings below should only be modified by a super admin. Super + admins can configure what tenant mode CIPP operates in. See + + our documentation + + for more information on how to configure these modes and what they mean. +

    +
    +
    + + +

    Tenant Mode

    + ( + <> + {partnerConfig.isFetching && } + + + + + + + )} + /> + {webhookCreateResult.isSuccess && ( + + {webhookCreateResult?.data?.results} + )} - /> - {webhookCreateResult.isSuccess && ( - - {webhookCreateResult?.data?.results} - - )} -
    -
    +
    +
    + - -
    + + + + + ) } diff --git a/src/views/cipp/app-settings/components/SettingsCustomRoles.jsx b/src/views/cipp/app-settings/components/SettingsCustomRoles.jsx new file mode 100644 index 000000000000..ad8bb5a8db2c --- /dev/null +++ b/src/views/cipp/app-settings/components/SettingsCustomRoles.jsx @@ -0,0 +1,351 @@ +import React, { useRef, useState } from 'react' +import { + CButton, + CCallout, + CCol, + CForm, + CRow, + CAccordion, + CAccordionHeader, + CAccordionBody, + CAccordionItem, +} from '@coreui/react' +import { Field, Form, FormSpy } from 'react-final-form' +import { RFFCFormRadioList, RFFSelectSearch } from 'src/components/forms' +import { useGenericGetRequestQuery, useLazyGenericPostRequestQuery } from 'src/store/api/app' +import { CippPage } from 'src/components/layout' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import Skeleton from 'react-loading-skeleton' +import { TenantSelectorMultiple, ModalService } from 'src/components/utilities' +import PropTypes from 'prop-types' +import { OnChange } from 'react-final-form-listeners' +import { useListTenantsQuery } from 'src/store/api/tenants' + +const SettingsCustomRoles = () => { + const [genericPostRequest, postResults] = useLazyGenericPostRequestQuery() + const [selectedTenant, setSelectedTenant] = useState([]) + const tenantSelectorRef = useRef() + const { data: tenants = [], tenantsFetching } = useListTenantsQuery({ + showAllTenantSelector: true, + }) + + const { + data: apiPermissions = [], + isFetching, + isSuccess, + } = useGenericGetRequestQuery({ + path: 'api/ExecAPIPermissionList', + }) + + const { + data: customRoleList = [], + isFetching: customRoleListFetching, + isSuccess: customRoleListSuccess, + refetch: refetchCustomRoleList, + } = useGenericGetRequestQuery({ + path: 'api/ExecCustomRole', + }) + + const handleSubmit = async (values) => { + //filter on only objects that are 'true' + genericPostRequest({ + path: '/api/ExecCustomRole?Action=AddUpdate', + values: { + RoleName: values.RoleName.value, + Permissions: values.Permissions, + AllowedTenants: selectedTenant.map((tenant) => tenant.value), + }, + }).then(() => { + refetchCustomRoleList() + }) + } + const handleDelete = async (values) => { + ModalService.confirm({ + title: 'Delete Custom Role', + body: 'Are you sure you want to delete this custom role? Any users with this role will have their permissions reset to the default for their base role.', + onConfirm: () => { + genericPostRequest({ + path: '/api/ExecCustomRole?Action=Delete', + values: { + RoleName: values.RoleName.value, + }, + }).then(() => { + refetchCustomRoleList() + }) + }, + }) + } + + const WhenFieldChanges = ({ field, set }) => ( + + {( + // No subscription. We only use Field to get to the change function + { input: { onChange } }, + ) => ( + + {({ form }) => ( + + {(value) => { + if (field === 'RoleName' && value?.value) { + let customRole = customRoleList.filter(function (obj) { + return obj.RowKey === value.value + }) + if (customRole === undefined || customRole === null || customRole.length === 0) { + return false + } else { + if (set === 'AllowedTenants') { + setSelectedTenant(customRole[0][set]) + var selectedTenants = [] + tenants.map((tenant) => { + if (customRole[0][set].includes(tenant.customerId)) { + selectedTenants.push({ + label: tenant.displayName, + value: tenant.customerId, + }) + } + }) + + tenantSelectorRef.current.setValue(selectedTenants) + } else { + onChange(customRole[0][set]) + } + } + } + if (field === 'Defaults') { + let newPermissions = {} + Object.keys(apiPermissions).forEach((cat) => { + Object.keys(apiPermissions[cat]).forEach((obj) => { + var newval = '' + if (cat == 'CIPP' && obj == 'Core' && value == 'None') { + newval = 'Read' + } else { + newval = value + } + newPermissions[`${cat}${obj}`] = `${cat}.${obj}.${newval}` + }) + }) + onChange(newPermissions) + } + }} + + )} + + )} + + ) + WhenFieldChanges.propTypes = { + field: PropTypes.node, + set: PropTypes.string, + } + return ( + + <> +

    + Custom roles can be used to restrict permissions for users with the 'editor' or 'readonly' + roles in CIPP. They can be limited to a subset of tenants and API permissions. +

    +

    + NOTE: The custom role must be added to the user in SWA in conjunction with the base role. + (e.g. editor,mycustomrole) +

    + {(isFetching || tenantsFetching) && } + {isSuccess && !isFetching && !tenantsFetching && ( + { + return ( + + + +
    + ({ + name: role.RowKey, + value: role.RowKey, + }))} + isLoading={customRoleListFetching} + refreshFunction={() => refetchCustomRoleList()} + allowCreate={true} + placeholder="Select an existing role or enter a custom role name" + /> + + +
    +
    +
    Allowed Tenants
    + setSelectedTenant(e)} + /> +
    +
    API Permissions
    + + +
    +
    Set All Permissions
    +
    +
    + + + + +
    + + <> + {Object.keys(apiPermissions) + .sort() + .map((cat, catIndex) => ( + + {cat} + + {Object.keys(apiPermissions[cat]) + .sort() + .map((obj, index) => ( + + +
    +
    {obj}
    +
    +
    + + + +
    + ))} +
    +
    + ))} + +
    +
    + + + {({ values }) => { + return ( + <> + {values['RoleName'] && selectedTenant.length > 0 && ( + <> +
    Selected Tenants
    +
      + {selectedTenant.map((tenant, idx) => ( +
    • {tenant.label}
    • + ))} +
    + + )} + {values['RoleName'] && values['Permissions'] && ( + <> +
    Selected Permissions
    +
      + {values['Permissions'] && + Object.keys(values['Permissions'])?.map((cat, idx) => ( + <> + {!values['Permissions'][cat].includes('None') && ( +
    • {values['Permissions'][cat]}
    • + )} + + ))} +
    + + )} + + ) + }} +
    +
    +
    + + {postResults.isSuccess && ( + {postResults.data.Results} + )} + + + + + Save + + + {({ values }) => { + return ( + handleDelete(values)} + disabled={!values['RoleName']} + > + + Delete + + ) + }} + + + + +
    + ) + }} + /> + )} + +
    + ) +} + +export default SettingsCustomRoles From 551ae1d27517526bcfe596774b76a42af03633f3 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Wed, 29 May 2024 01:18:21 -0400 Subject: [PATCH 210/475] Custom Role Info --- .../utilities/CippListOffcanvas.jsx | 9 +- .../components/SettingsCustomRoles.jsx | 123 +++++++++++++----- 2 files changed, 96 insertions(+), 36 deletions(-) diff --git a/src/components/utilities/CippListOffcanvas.jsx b/src/components/utilities/CippListOffcanvas.jsx index 4fbde6931ce3..e3c3229ae394 100644 --- a/src/components/utilities/CippListOffcanvas.jsx +++ b/src/components/utilities/CippListOffcanvas.jsx @@ -38,7 +38,7 @@ CippListOffcanvas.propTypes = { hideFunction: PropTypes.func.isRequired, } -export function OffcanvasListSection({ title, items }) { +export function OffcanvasListSection({ title, items, showCardTitle = true }) { //console.log(items) const mappedItems = items.map((item, key) => ({ value: item.content, label: item.heading })) return ( @@ -48,7 +48,11 @@ export function OffcanvasListSection({ title, items }) { - Extended Information + {showCardTitle && ( + <> + Extended Information + + )} @@ -62,4 +66,5 @@ export function OffcanvasListSection({ title, items }) { OffcanvasListSection.propTypes = { title: PropTypes.string, items: PropTypes.array, + showCardTitle: PropTypes.bool, } diff --git a/src/views/cipp/app-settings/components/SettingsCustomRoles.jsx b/src/views/cipp/app-settings/components/SettingsCustomRoles.jsx index ad8bb5a8db2c..55215fa1400f 100644 --- a/src/views/cipp/app-settings/components/SettingsCustomRoles.jsx +++ b/src/views/cipp/app-settings/components/SettingsCustomRoles.jsx @@ -16,10 +16,11 @@ import { useGenericGetRequestQuery, useLazyGenericPostRequestQuery } from 'src/s import { CippPage } from 'src/components/layout' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import Skeleton from 'react-loading-skeleton' -import { TenantSelectorMultiple, ModalService } from 'src/components/utilities' +import { TenantSelectorMultiple, ModalService, CippOffcanvas } from 'src/components/utilities' import PropTypes from 'prop-types' import { OnChange } from 'react-final-form-listeners' import { useListTenantsQuery } from 'src/store/api/tenants' +import CippListOffcanvas, { OffcanvasListSection } from 'src/components/utilities/CippListOffcanvas' const SettingsCustomRoles = () => { const [genericPostRequest, postResults] = useLazyGenericPostRequestQuery() @@ -137,6 +138,85 @@ const SettingsCustomRoles = () => { field: PropTypes.node, set: PropTypes.string, } + + const ApiPermissionRow = ({ obj, cat }) => { + const [offcanvasVisible, setOffcanvasVisible] = useState(false) + + var items = [] + for (var key in apiPermissions[cat][obj]) + for (var key2 in apiPermissions[cat][obj][key]) { + items.push({ heading: '', content: apiPermissions[cat][obj][key][key2] }) + } + var group = [{ items: items }] + + return ( + <> + +
    +
    {obj}
    +
    +
    + + setOffcanvasVisible(true)} variant="ghost" size="sm" color="info"> + + + + + + + setOffcanvasVisible(false)} + title={`${cat}.${obj}`} + placement="end" + size="lg" + > +

    + Listed below are the available API endpoints based on permission level, ReadWrite level + includes endpoints under Read. +

    + {[apiPermissions[cat][obj]].map((permissions, key) => { + var sections = Object.keys(permissions).map((type) => { + var items = [] + for (var api in permissions[type]) { + items.push({ heading: '', content: permissions[type][api] }) + } + return ( + + ) + }) + return sections + })} +
    + + ) + } + ApiPermissionRow.propTypes = { + obj: PropTypes.node, + cat: PropTypes.node, + } + return ( <> @@ -196,6 +276,7 @@ const SettingsCustomRoles = () => {
    Set All Permissions
    + { {Object.keys(apiPermissions[cat]) .sort() - .map((obj, index) => ( - - -
    -
    {obj}
    -
    -
    - - - -
    - ))} + .map((obj, index) => { + return ( + + + + ) + })}
    ))} From 17dcf396b805f66b89c5c3348fe482fa0e46d215 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Wed, 29 May 2024 09:18:01 -0400 Subject: [PATCH 211/475] Update SettingsCustomRoles.jsx --- .../cipp/app-settings/components/SettingsCustomRoles.jsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/views/cipp/app-settings/components/SettingsCustomRoles.jsx b/src/views/cipp/app-settings/components/SettingsCustomRoles.jsx index 55215fa1400f..6f439a01c832 100644 --- a/src/views/cipp/app-settings/components/SettingsCustomRoles.jsx +++ b/src/views/cipp/app-settings/components/SettingsCustomRoles.jsx @@ -188,10 +188,11 @@ const SettingsCustomRoles = () => { setOffcanvasVisible(false)} - title={`${cat}.${obj}`} + title="Permission Info" placement="end" size="lg" > +

    {`${cat}.${obj}`}

    Listed below are the available API endpoints based on permission level, ReadWrite level includes endpoints under Read. @@ -222,7 +223,8 @@ const SettingsCustomRoles = () => { <>

    Custom roles can be used to restrict permissions for users with the 'editor' or 'readonly' - roles in CIPP. They can be limited to a subset of tenants and API permissions. + roles in CIPP. They can be limited to a subset of tenants and API permissions. To restrict + direct API access, create a role with the name 'CIPP-API'.

    NOTE: The custom role must be added to the user in SWA in conjunction with the base role. From 6ed7aa3aea3b537c539b4099984e02714ba5c425 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Wed, 29 May 2024 13:10:38 -0400 Subject: [PATCH 212/475] Fix scripted alert react error --- src/views/tenant/administration/AlertWizard.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/views/tenant/administration/AlertWizard.jsx b/src/views/tenant/administration/AlertWizard.jsx index 2c3bf20bf0cf..6b1daf2e77ca 100644 --- a/src/views/tenant/administration/AlertWizard.jsx +++ b/src/views/tenant/administration/AlertWizard.jsx @@ -317,7 +317,7 @@ const AlertWizard = () => { multi={true} name={`actions`} placeholder={ - 'Select one action or multple actions from the list' + 'Select one action or multiple actions from the list' } label="Then perform the following action(s)" /> @@ -382,7 +382,7 @@ const AlertWizard = () => { /> - + From 2b7edbd95bcd5a2e4f87c093c74c6fec17db81db Mon Sep 17 00:00:00 2001 From: John Duprey Date: Wed, 29 May 2024 13:18:24 -0400 Subject: [PATCH 213/475] Add beta notice --- src/views/cipp/app-settings/SettingsSuperAdmin.jsx | 2 +- .../cipp/app-settings/components/SettingsCustomRoles.jsx | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/views/cipp/app-settings/SettingsSuperAdmin.jsx b/src/views/cipp/app-settings/SettingsSuperAdmin.jsx index bec964fa3386..8f1184eef32d 100644 --- a/src/views/cipp/app-settings/SettingsSuperAdmin.jsx +++ b/src/views/cipp/app-settings/SettingsSuperAdmin.jsx @@ -103,7 +103,7 @@ export function SettingsSuperAdmin() { - + diff --git a/src/views/cipp/app-settings/components/SettingsCustomRoles.jsx b/src/views/cipp/app-settings/components/SettingsCustomRoles.jsx index 6f439a01c832..f45741bcabed 100644 --- a/src/views/cipp/app-settings/components/SettingsCustomRoles.jsx +++ b/src/views/cipp/app-settings/components/SettingsCustomRoles.jsx @@ -227,8 +227,9 @@ const SettingsCustomRoles = () => { direct API access, create a role with the name 'CIPP-API'.

    - NOTE: The custom role must be added to the user in SWA in conjunction with the base role. - (e.g. editor,mycustomrole) + This functionality is in + beta and should be treated as such. The custom role must be added to the user in SWA in + conjunction with the base role. (e.g. editor,mycustomrole)

    {(isFetching || tenantsFetching) && } {isSuccess && !isFetching && !tenantsFetching && ( From 2e671927c16112c89c88ba2e8f3db96d3814bad2 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Wed, 29 May 2024 14:37:08 -0400 Subject: [PATCH 214/475] Fix meta.error on RFFSelectSearch --- src/components/forms/RFFComponents.jsx | 6 +++++- src/views/tenant/administration/AlertWizard.jsx | 3 +-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/components/forms/RFFComponents.jsx b/src/components/forms/RFFComponents.jsx index 01b4ed8f083b..b72a840d4989 100644 --- a/src/components/forms/RFFComponents.jsx +++ b/src/components/forms/RFFComponents.jsx @@ -587,7 +587,11 @@ export const RFFSelectSearch = ({ {...props} /> )} - {meta.error && meta.touched && {meta.error}} + {meta.error && meta.touched && ( + + {typeof meta.error === 'object' ? Object.values(meta.error).join('') : meta.error} + + )}
    ) }} diff --git a/src/views/tenant/administration/AlertWizard.jsx b/src/views/tenant/administration/AlertWizard.jsx index 6b1daf2e77ca..549a316beb1f 100644 --- a/src/views/tenant/administration/AlertWizard.jsx +++ b/src/views/tenant/administration/AlertWizard.jsx @@ -96,7 +96,6 @@ const AlertWizard = () => { const getRecurrenceOptions = () => { const values = currentFormState?.values if (values) { - //console.log(currentFormState) const updatedRecurrenceOptions = recurrenceOptions.map((opt) => ({ ...opt, name: opt.name.replace(' (Recommended)', ''), @@ -382,7 +381,7 @@ const AlertWizard = () => { />
    - + From 25688a661425e06aa1cc602cb8a21b685b91cf1e Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Thu, 30 May 2024 00:21:28 +0200 Subject: [PATCH 215/475] remove accordian for superadmin --- src/views/cipp/app-settings/SettingsSuperAdmin.jsx | 13 ++++++------- .../app-settings/components/SettingsCustomRoles.jsx | 7 ++++--- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/views/cipp/app-settings/SettingsSuperAdmin.jsx b/src/views/cipp/app-settings/SettingsSuperAdmin.jsx index 8f1184eef32d..4e38038fb68c 100644 --- a/src/views/cipp/app-settings/SettingsSuperAdmin.jsx +++ b/src/views/cipp/app-settings/SettingsSuperAdmin.jsx @@ -6,6 +6,7 @@ import React from 'react' import { CippCallout } from 'src/components/layout/index.js' import CippAccordionItem from 'src/components/contentcards/CippAccordionItem' import SettingsCustomRoles from 'src/views/cipp/app-settings/components/SettingsCustomRoles' +import CippButtonCard from 'src/components/contentcards/CippButtonCard' export function SettingsSuperAdmin() { const partnerConfig = useGenericGetRequestQuery({ @@ -39,8 +40,8 @@ export function SettingsSuperAdmin() { ) return ( - - + - - - - - + + + ) } diff --git a/src/views/cipp/app-settings/components/SettingsCustomRoles.jsx b/src/views/cipp/app-settings/components/SettingsCustomRoles.jsx index f45741bcabed..d4b8d240a668 100644 --- a/src/views/cipp/app-settings/components/SettingsCustomRoles.jsx +++ b/src/views/cipp/app-settings/components/SettingsCustomRoles.jsx @@ -21,6 +21,7 @@ import PropTypes from 'prop-types' import { OnChange } from 'react-final-form-listeners' import { useListTenantsQuery } from 'src/store/api/tenants' import CippListOffcanvas, { OffcanvasListSection } from 'src/components/utilities/CippListOffcanvas' +import CippButtonCard from 'src/components/contentcards/CippButtonCard' const SettingsCustomRoles = () => { const [genericPostRequest, postResults] = useLazyGenericPostRequestQuery() @@ -219,7 +220,7 @@ const SettingsCustomRoles = () => { } return ( - + <>

    Custom roles can be used to restrict permissions for users with the 'editor' or 'readonly' @@ -231,7 +232,6 @@ const SettingsCustomRoles = () => { beta and should be treated as such. The custom role must be added to the user in SWA in conjunction with the base role. (e.g. editor,mycustomrole)

    - {(isFetching || tenantsFetching) && } {isSuccess && !isFetching && !tenantsFetching && ( {
    + {({ values }) => { @@ -402,7 +403,7 @@ const SettingsCustomRoles = () => { /> )} - + ) } From 0c07f2cccae1a64f8509f9c57f088b1c83c267d7 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Wed, 29 May 2024 20:46:39 -0400 Subject: [PATCH 216/475] Update ListAppliedStandards.jsx --- src/views/tenant/standards/ListAppliedStandards.jsx | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/views/tenant/standards/ListAppliedStandards.jsx b/src/views/tenant/standards/ListAppliedStandards.jsx index 484ec3862a36..c48dd3705753 100644 --- a/src/views/tenant/standards/ListAppliedStandards.jsx +++ b/src/views/tenant/standards/ListAppliedStandards.jsx @@ -634,20 +634,13 @@ const ApplyNewStandard = () => {
    Remediate
    From 875d47b633e4f5918519e3123a2da4fcf8bb7338 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 30 May 2024 00:05:44 -0400 Subject: [PATCH 217/475] Custom role callouts --- .../components/SettingsCustomRoles.jsx | 35 ++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/src/views/cipp/app-settings/components/SettingsCustomRoles.jsx b/src/views/cipp/app-settings/components/SettingsCustomRoles.jsx index d4b8d240a668..ff0b888eed72 100644 --- a/src/views/cipp/app-settings/components/SettingsCustomRoles.jsx +++ b/src/views/cipp/app-settings/components/SettingsCustomRoles.jsx @@ -30,6 +30,8 @@ const SettingsCustomRoles = () => { const { data: tenants = [], tenantsFetching } = useListTenantsQuery({ showAllTenantSelector: true, }) + const [allTenantSelected, setAllTenantSelected] = useState(false) + const [cippApiRoleSelected, setCippApiRoleSelected] = useState(false) const { data: apiPermissions = [], @@ -48,6 +50,20 @@ const SettingsCustomRoles = () => { path: 'api/ExecCustomRole', }) + const handleTenantChange = (e) => { + var alltenant = false + e.map((tenant) => { + if (tenant.value === 'AllTenants') { + alltenant = true + } + }) + if (alltenant) { + setAllTenantSelected(true) + } else { + setAllTenantSelected(false) + } + setSelectedTenant(e) + } const handleSubmit = async (values) => { //filter on only objects that are 'true' genericPostRequest({ @@ -92,6 +108,12 @@ const SettingsCustomRoles = () => { let customRole = customRoleList.filter(function (obj) { return obj.RowKey === value.value }) + if (customRole[0].RowKey === 'CIPP-API') { + setCippApiRoleSelected(true) + } else { + setCippApiRoleSelected(false) + } + if (customRole === undefined || customRole === null || customRole.length === 0) { return false } else { @@ -255,6 +277,12 @@ const SettingsCustomRoles = () => { /> + {cippApiRoleSelected && ( + + This role will limit access for the CIPP-API integration. It is not + intended to be used for users. + + )}
    Allowed Tenants
    @@ -263,8 +291,13 @@ const SettingsCustomRoles = () => { values={selectedTenant} AllTenants={true} valueIsDomain={true} - onChange={(e) => setSelectedTenant(e)} + onChange={(e) => handleTenantChange(e)} /> + {allTenantSelected && ( + + All tenants selected, no tenant restrictions will be applied. + + )}
    API Permissions
    From 7a12be5d5da6375d686d5d516037dbf2d1b34bc6 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 30 May 2024 00:08:27 -0400 Subject: [PATCH 218/475] Update SettingsCustomRoles.jsx --- src/views/cipp/app-settings/components/SettingsCustomRoles.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/cipp/app-settings/components/SettingsCustomRoles.jsx b/src/views/cipp/app-settings/components/SettingsCustomRoles.jsx index ff0b888eed72..75c0a7504487 100644 --- a/src/views/cipp/app-settings/components/SettingsCustomRoles.jsx +++ b/src/views/cipp/app-settings/components/SettingsCustomRoles.jsx @@ -108,7 +108,7 @@ const SettingsCustomRoles = () => { let customRole = customRoleList.filter(function (obj) { return obj.RowKey === value.value }) - if (customRole[0].RowKey === 'CIPP-API') { + if (customRole[0]?.RowKey === 'CIPP-API') { setCippApiRoleSelected(true) } else { setCippApiRoleSelected(false) From 9f62c0ae203bbc350d3e3b365e42b6a4a40fad2f Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Thu, 30 May 2024 13:07:45 +0200 Subject: [PATCH 219/475] new extension config page --- src/_nav.jsx | 5 + src/data/Extensions.json | 12 +- src/importsMap.jsx | 1 + src/routes.json | 6 + src/views/cipp/Extensions.jsx | 173 +++++ .../SettingsExtensionMappings.jsx | 621 +++++++++--------- .../cipp/app-settings/SettingsExtensions.jsx | 16 +- 7 files changed, 513 insertions(+), 321 deletions(-) create mode 100644 src/views/cipp/Extensions.jsx diff --git a/src/_nav.jsx b/src/_nav.jsx index 902a8390b79f..81175e8886f0 100644 --- a/src/_nav.jsx +++ b/src/_nav.jsx @@ -771,6 +771,11 @@ const _nav = [ name: 'Application Settings', to: '/cipp/settings', }, + { + component: CNavItem, + name: 'Extensions Settings', + to: '/cipp/extensions', + }, { component: CNavItem, name: 'User Settings', diff --git a/src/data/Extensions.json b/src/data/Extensions.json index 0f26e93f26a5..98a5c9a5a3f2 100644 --- a/src/data/Extensions.json +++ b/src/data/Extensions.json @@ -16,7 +16,8 @@ "name": "cippapi.Enabled", "label": "Enable Integration" } - ] + ], + "mappingRequired": false }, { "name": "Gradient Integration", @@ -49,7 +50,8 @@ "name": "Gradient.Enabled", "label": "Enable Integration" } - ] + ], + "mappingRequired": false }, { "name": "Halo PSA Ticketing Integration", @@ -105,7 +107,8 @@ "name": "HaloPSA.Enabled", "label": "Enable Integration" } - ] + ], + "mappingRequired": true }, { "name": "NinjaOne Integration", @@ -155,6 +158,7 @@ "name": "NinjaOne.Enabled", "label": "Enable Integration" } - ] + ], + "mappingRequired": true } ] diff --git a/src/importsMap.jsx b/src/importsMap.jsx index fd44014ae9c7..a9dd3c4843d9 100644 --- a/src/importsMap.jsx +++ b/src/importsMap.jsx @@ -129,6 +129,7 @@ import React from 'react' "/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/extensions": React.lazy(() => import('./views/cipp/Extensions')), "/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')), diff --git a/src/routes.json b/src/routes.json index e681555e0346..26c0ed1b027f 100644 --- a/src/routes.json +++ b/src/routes.json @@ -888,6 +888,12 @@ "component": "views/cipp/app-settings/CIPPSettings", "allowedRoles": ["admin"] }, + { + "path": "/cipp/extensions", + "name": "Extensions Settings", + "component": "views/cipp/Extensions", + "allowedRoles": ["admin"] + }, { "path": "/cipp/setup", "name": "Setup", diff --git a/src/views/cipp/Extensions.jsx b/src/views/cipp/Extensions.jsx new file mode 100644 index 000000000000..d42e79d9bb38 --- /dev/null +++ b/src/views/cipp/Extensions.jsx @@ -0,0 +1,173 @@ +import React, { useRef, useState } from 'react' +import { + CButton, + CCardText, + CCol, + CForm, + CNav, + CNavItem, + CRow, + CTabContent, + CTabPane, +} from '@coreui/react' +import { CippPage } from 'src/components/layout' +import { CippLazy } from 'src/components/utilities' +import { useNavigate } from 'react-router-dom' +import useQuery from 'src/hooks/useQuery.jsx' +import Extensions from 'src/data/Extensions.json' +import { useLazyGenericGetRequestQuery, useLazyGenericPostRequestQuery } from 'src/store/api/app.js' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { faCircleNotch } from '@fortawesome/free-solid-svg-icons' +import CippButtonCard from 'src/components/contentcards/CippButtonCard.jsx' +import { RFFCFormInput, RFFCFormSwitch } from 'src/components/forms/RFFComponents.jsx' +import { Form } from 'react-final-form' +import { SettingsExtensionMappings } from './app-settings/SettingsExtensionMappings' + +export default function CIPPExtensions() { + const [listBackend, listBackendResult] = useLazyGenericGetRequestQuery() + const inputRef = useRef(null) + const [setExtensionconfig, extensionConfigResult] = useLazyGenericPostRequestQuery() + const [execTestExtension, listExtensionTestResult] = useLazyGenericGetRequestQuery() + const [execSyncExtension, listSyncExtensionResult] = useLazyGenericGetRequestQuery() + + const onSubmitTest = (integrationName) => { + execTestExtension({ + path: 'api/ExecExtensionTest?extensionName=' + integrationName, + }) + } + const onSubmit = (values) => { + setExtensionconfig({ + path: 'api/ExecExtensionsConfig', + values: values, + }) + } + + const ButtonGenerate = (integrationType, forceSync) => ( + <> + + {extensionConfigResult.isFetching && ( + + )} + Set Extension Settings + + onSubmitTest(integrationType)} className="me-2"> + {listExtensionTestResult.isFetching && ( + + )} + Test Extension + + {forceSync && ( + + execSyncExtension({ + path: 'api/ExecExtensionSync?Extension=' + integrationType, + }) + } + className="me-2" + > + {listSyncExtensionResult.isFetching && ( + + )} + Force Sync + + )} + + ) + const queryString = useQuery() + const navigate = useNavigate() + + const tab = queryString.get('tab') + const [active, setActiveTab] = useState(tab ? parseInt(tab) : 0) + const setActive = (tab) => { + setActiveTab(tab) + queryString.set('tab', tab.toString()) + navigate(`${location.pathname}?${queryString}`) + } + + return ( + + {listBackendResult.isUninitialized && listBackend({ path: 'api/ListExtensionsConfig' })} + + {Extensions.map((integration, idx) => ( + setActive(idx)} + href="#" + > + {integration.name} + + ))} + + + {Extensions.map((integration, idx) => ( + + + + + +

    {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/SettingsExtensionMappings.jsx b/src/views/cipp/app-settings/SettingsExtensionMappings.jsx index 33d7491795bf..6d7a63d2a3d0 100644 --- a/src/views/cipp/app-settings/SettingsExtensionMappings.jsx +++ b/src/views/cipp/app-settings/SettingsExtensionMappings.jsx @@ -19,13 +19,14 @@ import { CippCallout } from 'src/components/layout/index.js' import CippAccordionItem from 'src/components/contentcards/CippAccordionItem' import { CippTable } from 'src/components/tables' import { CellTip } from 'src/components/tables/CellGenericFormat' +import CippButtonCard from 'src/components/contentcards/CippButtonCard' /** * Retrieves and sets the extension mappings for HaloPSA and NinjaOne. * * @returns {JSX.Element} - JSX component representing the settings extension mappings. */ -export function SettingsExtensionMappings() { +export function SettingsExtensionMappings({ type }) { const [addedAttributes, setAddedAttribute] = React.useState(1) const [mappingArray, setMappingArray] = React.useState('defaultMapping') const [mappingValue, setMappingValue] = React.useState({}) @@ -242,310 +243,326 @@ export function SettingsExtensionMappings() { return ( - {listBackendHaloResult.isUninitialized && - listHaloBackend({ path: 'api/ExecExtensionMapping?List=Halo' })} - {listBackendNinjaOrgsResult.isUninitialized && - 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 && 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) + {type === 'HaloPSA' && ( + <> + {listBackendHaloResult.isUninitialized && + listHaloBackend({ path: 'api/ExecExtensionMapping?List=Halo' })} + + + + {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 && + mappingValue.value !== '-1' && + Object.values(haloMappingsArray) + .map((item) => item.haloId) + .includes(mappingValue.value) === false + ) { + //set the new mapping in the array + setHaloMappingsArray([ + ...haloMappingsArray, + { + Tenant: listBackendHaloResult.data?.Tenants.find( + (tenant) => tenant.customerId === mappingArray, + ), + haloName: mappingValue.label, + haloId: mappingValue.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 && - mappingValue.value !== '-1' && - Object.values(haloMappingsArray) - .map((item) => item.haloId) - .includes(mappingValue.value) === false - ) { - //set the new mapping in the array - setHaloMappingsArray([ - ...haloMappingsArray, - { - Tenant: listBackendHaloResult.data?.Tenants.find( - (tenant) => tenant.customerId === mappingArray, - ), - haloName: mappingValue.label, - haloId: mappingValue.value, - }, - ]) - } - }} - 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 && ( - - {extensionHaloConfigResult.isSuccess - ? extensionHaloConfigResult.data.Results - : 'Error'} - + + + + + + {HaloAutoMap && ( + + Automapping has been executed. Remember to check the changes and save + them. + )} - - - - 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 && - mappingValue.value !== '-1' && - Object.values(ninjaMappingsArray) - .map((item) => item.ninjaId) - .includes(mappingValue.value) === false - ) { - setNinjaMappingsArray([ - ...ninjaMappingsArray, - { - Tenant: listBackendNinjaOrgsResult.data?.Tenants.find( - (tenant) => tenant.customerId === mappingArray, - ), - ninjaName: mappingValue.label, - ninjaId: mappingValue.value, + {(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. + + + ) + }} + /> + )} + + + )} + {type === 'NinjaOne' && ( + <> + {listBackendNinjaOrgsResult.isUninitialized && + listNinjaOrgsBackend({ path: 'api/ExecExtensionMapping?List=NinjaOrgs' })} + {listBackendNinjaFieldsResult.isUninitialized && + listNinjaFieldsBackend({ path: 'api/ExecExtensionMapping?List=NinjaFields' })} + + + {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()) }, - ]) - } - }} - className={`my-4 circular-button`} - title={'+'} - > - - - - - - {(extensionNinjaOrgsAutomapResult.isSuccess || - extensionNinjaOrgsAutomapResult.isError) && - !extensionNinjaOrgsAutomapResult.isFetching && ( - - {extensionNinjaOrgsAutomapResult.isSuccess - ? extensionNinjaOrgsAutomapResult.data.Results - : 'Error'} - - )} - {(extensionNinjaOrgsConfigResult.isSuccess || - extensionNinjaOrgsConfigResult.isError) && - !extensionNinjaOrgsConfigResult.isFetching && ( - ({ + 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 && + mappingValue.value !== '-1' && + Object.values(ninjaMappingsArray) + .map((item) => item.ninjaId) + .includes(mappingValue.value) === false + ) { + setNinjaMappingsArray([ + ...ninjaMappingsArray, + { + Tenant: listBackendNinjaOrgsResult.data?.Tenants.find( + (tenant) => tenant.customerId === mappingArray, + ), + ninjaName: mappingValue.label, + ninjaId: mappingValue.value, + }, + ]) + } + }} + className={`my-4 circular-button`} + title={'+'} > - {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. - - - ) - }} - /> - )} - - + +
    + + + {(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. + + + ) + }} + /> + )} + + + )} + {type === 'NinjaOne' && ( + )} - - + + )} ) } diff --git a/src/views/cipp/app-settings/SettingsExtensions.jsx b/src/views/cipp/app-settings/SettingsExtensions.jsx index 20ebbcee9725..ab686d2c1df9 100644 --- a/src/views/cipp/app-settings/SettingsExtensions.jsx +++ b/src/views/cipp/app-settings/SettingsExtensions.jsx @@ -1,20 +1,6 @@ import { useLazyGenericGetRequestQuery, useLazyGenericPostRequestQuery } from 'src/store/api/app.js' import React, { useRef } from 'react' -import { - CAccordion, - CAlert, - CButton, - CCallout, - CCard, - CCardBody, - CCardHeader, - CCardText, - CCardTitle, - CCol, - CForm, - CRow, - CSpinner, -} from '@coreui/react' +import { CAccordion, CButton, CCardText, CCol, CForm, CSpinner } from '@coreui/react' import Extensions from 'src/data/Extensions.json' import { Form } from 'react-final-form' import { RFFCFormInput, RFFCFormSwitch } from 'src/components/forms/index.js' From 40d54d669db2a636a84c93a64fd2a238c9b757bf Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Thu, 30 May 2024 15:56:09 +0200 Subject: [PATCH 220/475] remove old extensions --- src/views/cipp/app-settings/CIPPSettings.jsx | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/src/views/cipp/app-settings/CIPPSettings.jsx b/src/views/cipp/app-settings/CIPPSettings.jsx index 965e1f98f11f..c7e7a227f75e 100644 --- a/src/views/cipp/app-settings/CIPPSettings.jsx +++ b/src/views/cipp/app-settings/CIPPSettings.jsx @@ -58,12 +58,6 @@ export default function CIPPSettings() { setActive(7)} href="#"> Maintenance - setActive(8)} href="#"> - Extensions - - setActive(9)} href="#"> - Extension Mappings - {superAdmin && ( setActive(10)} href="#"> SuperAdmin Settings @@ -104,16 +98,6 @@ export default function CIPPSettings() { - - - - - - - - - - From 15dcc67217176aeab87ab07d45bbccd6ba527a5f Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 30 May 2024 10:00:54 -0400 Subject: [PATCH 221/475] JIT admin route/nav --- src/_nav.jsx | 5 +++++ src/importsMap.jsx | 1 + src/routes.json | 6 ++++++ 3 files changed, 12 insertions(+) diff --git a/src/_nav.jsx b/src/_nav.jsx index 81175e8886f0..04fa3f3866a1 100644 --- a/src/_nav.jsx +++ b/src/_nav.jsx @@ -75,6 +75,11 @@ const _nav = [ name: 'Roles', to: '/identity/administration/roles', }, + { + component: CNavItem, + name: 'JIT Admin', + to: '/identity/administration/users/jit-admin', + }, { component: CNavItem, name: 'Offboarding Wizard', diff --git a/src/importsMap.jsx b/src/importsMap.jsx index a9dd3c4843d9..d42693788afb 100644 --- a/src/importsMap.jsx +++ b/src/importsMap.jsx @@ -12,6 +12,7 @@ import React from 'react' "/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/users/jit-admin": React.lazy(() => import('./views/identity/administration/DeployJITAdmin')), "/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')), diff --git a/src/routes.json b/src/routes.json index 26c0ed1b027f..61c736f9c23d 100644 --- a/src/routes.json +++ b/src/routes.json @@ -76,6 +76,12 @@ "component": "views/identity/administration/InviteGuest", "allowedRoles": ["admin", "editor", "readonly"] }, + { + "path": "/identity/administration/users/jit-admin", + "name": "JIT Admin", + "component": "views/identity/administration/DeployJITAdmin", + "allowedRoles": ["admin", "editor", "readonly"] + }, { "path": "/identity/administration/ViewBec", "name": "View BEC", From c74cd41b543258011a13501e16d37cbd0dde2196 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 30 May 2024 10:01:05 -0400 Subject: [PATCH 222/475] Remove extension tabs from settings --- src/views/cipp/app-settings/CIPPSettings.jsx | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/src/views/cipp/app-settings/CIPPSettings.jsx b/src/views/cipp/app-settings/CIPPSettings.jsx index 965e1f98f11f..14e5c3e0cb9e 100644 --- a/src/views/cipp/app-settings/CIPPSettings.jsx +++ b/src/views/cipp/app-settings/CIPPSettings.jsx @@ -58,14 +58,8 @@ export default function CIPPSettings() { setActive(7)} href="#"> Maintenance - setActive(8)} href="#"> - Extensions - - setActive(9)} href="#"> - Extension Mappings - {superAdmin && ( - setActive(10)} href="#"> + setActive(8)} href="#"> SuperAdmin Settings )} @@ -106,16 +100,6 @@ export default function CIPPSettings() { - - - - - - - - - - From a6c1f615152e68b6703378280575d71dcbd0b293 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 30 May 2024 10:09:52 -0400 Subject: [PATCH 223/475] fix nav/route --- src/_nav.jsx | 2 +- src/routes.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/_nav.jsx b/src/_nav.jsx index 04fa3f3866a1..ada800050fc5 100644 --- a/src/_nav.jsx +++ b/src/_nav.jsx @@ -78,7 +78,7 @@ const _nav = [ { component: CNavItem, name: 'JIT Admin', - to: '/identity/administration/users/jit-admin', + to: '/identity/administration/jit-admin', }, { component: CNavItem, diff --git a/src/routes.json b/src/routes.json index 61c736f9c23d..f27aaa5b148e 100644 --- a/src/routes.json +++ b/src/routes.json @@ -77,7 +77,7 @@ "allowedRoles": ["admin", "editor", "readonly"] }, { - "path": "/identity/administration/users/jit-admin", + "path": "/identity/administration/jit-admin", "name": "JIT Admin", "component": "views/identity/administration/DeployJITAdmin", "allowedRoles": ["admin", "editor", "readonly"] From 7be941d0ab1eb5530e1270763ef69c44657c3c42 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Thu, 30 May 2024 18:40:12 +0200 Subject: [PATCH 224/475] fixes bpa if never run --- .../tenant/standards/BestPracticeAnalyser.jsx | 198 ++++++++++-------- 1 file changed, 107 insertions(+), 91 deletions(-) diff --git a/src/views/tenant/standards/BestPracticeAnalyser.jsx b/src/views/tenant/standards/BestPracticeAnalyser.jsx index 10e7c480c29f..801cd1eb6838 100644 --- a/src/views/tenant/standards/BestPracticeAnalyser.jsx +++ b/src/views/tenant/standards/BestPracticeAnalyser.jsx @@ -346,99 +346,115 @@ const BestPracticeAnalyser = () => { refreshFunction={setRefreshValue} />
    - {graphrequest.data.Columns.map((info, idx) => ( - - - - {info.name} - - - - {info.formatter === 'bool' && ( - - - {graphrequest.data.Data[0][info.value] ? 'Yes' : 'No'} - - )} - {info.formatter === 'reverseBool' && ( - - - {graphrequest.data.Data[0][info.value] ? 'No' : 'Yes'} - - )} - {info.formatter === 'warnBool' && ( - - - {graphrequest.data.Data[0][info.value] ? 'Yes' : 'No'} - - )} + {graphrequest.data?.Data[0] && + Object.keys(graphrequest.data.Data[0]).length === 0 ? ( + + + Best Practice Report + + + + No Data Found for this tenant. Please refresh the tenant data. + + + + ) : ( + graphrequest.data.Columns.map((info, idx) => ( + + + + {info.name} + + + + {info.formatter === 'bool' && ( + + + {graphrequest.data.Data[0][info.value] ? 'Yes' : 'No'} + + )} + {info.formatter === 'reverseBool' && ( + + + {graphrequest.data.Data[0][info.value] ? 'No' : 'Yes'} + + )} + {info.formatter === 'warnBool' && ( + + + {graphrequest.data.Data[0][info.value] ? 'Yes' : 'No'} + + )} - {info.formatter === 'table' && ( - <> - - - )} + {info.formatter === 'table' && ( + <> + + + )} - {info.formatter === 'number' && ( -

    - {getNestedValue(graphrequest.data.Data[0], info.value)} -

    - )} -
    - - {info.desc} - -
    -
    -
    - ))} + {info.formatter === 'number' && ( +

    + {getNestedValue(graphrequest.data.Data[0], info.value)} +

    + )} +
    + + {info.desc} + +
    +
    +
    + )) + )} )} From 15986ade977c349ab744270d3249d682e24c6a35 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Wed, 29 May 2024 20:46:39 -0400 Subject: [PATCH 225/475] Update ListAppliedStandards.jsx --- src/views/tenant/standards/ListAppliedStandards.jsx | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/views/tenant/standards/ListAppliedStandards.jsx b/src/views/tenant/standards/ListAppliedStandards.jsx index 484ec3862a36..c48dd3705753 100644 --- a/src/views/tenant/standards/ListAppliedStandards.jsx +++ b/src/views/tenant/standards/ListAppliedStandards.jsx @@ -634,20 +634,13 @@ const ApplyNewStandard = () => {
    Remediate
    From c11dd30e4dd9d4030a7cacbfbbdddeaf74fefb13 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Fri, 31 May 2024 13:32:16 -0400 Subject: [PATCH 226/475] up version --- package.json | 2 +- public/version_latest.txt | 2 +- version_latest.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 332ac7504f6e..76ec78f43af2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cipp", - "version": "5.7.0", + "version": "5.7.1", "description": "The CyberDrain Improved Partner Portal is a portal to help manage administration for Microsoft Partners.", "homepage": "https://cipp.app/", "bugs": { diff --git a/public/version_latest.txt b/public/version_latest.txt index 42cdd0b540f9..64ff7ded70d3 100644 --- a/public/version_latest.txt +++ b/public/version_latest.txt @@ -1 +1 @@ -5.7.0 +5.7.1 diff --git a/version_latest.txt b/version_latest.txt index 42cdd0b540f9..64ff7ded70d3 100644 --- a/version_latest.txt +++ b/version_latest.txt @@ -1 +1 @@ -5.7.0 +5.7.1 From 4c031850e805fd7aec6884c043508bfc442c2237 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Kj=C3=A6rg=C3=A5rd?= Date: Fri, 31 May 2024 19:55:53 +0200 Subject: [PATCH 227/475] Add CloudMessageRecall standard --- src/data/standards.json | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/data/standards.json b/src/data/standards.json index 3efbc09ea197..3278c5631af2 100644 --- a/src/data/standards.json +++ b/src/data/standards.json @@ -632,6 +632,32 @@ "impact": "Low Impact", "impactColour": "info" }, + { + "name": "standards.CloudMessageRecall", + "cat": "Exchange Standards", + "tag": ["lowimpact"], + "helpText": "Sets the Cloud Message Recall state for the tenant. This allows users to recall messages from the cloud.", + "addedComponent": [ + { + "type": "Select", + "label": "Select value", + "name": "standards.CloudMessageRecall.state", + "values": [ + { + "label": "Enabled", + "value": "true" + }, + { + "label": "Disabled", + "value": "false" + } + ] + } + ], + "label": "Set Cloud Message Recall state", + "impact": "Low Impact", + "impactColour": "info" + }, { "name": "standards.AutoExpandArchive", "cat": "Exchange Standards", From 457022f47e5bf33c54c464479a061351c3355568 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Tue, 28 May 2024 12:12:51 +0200 Subject: [PATCH 228/475] improvements to autopilot. --- .../endpoint/autopilot/AutopilotAddDevice.jsx | 36 +++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/src/views/endpoint/autopilot/AutopilotAddDevice.jsx b/src/views/endpoint/autopilot/AutopilotAddDevice.jsx index 35abc7cc505f..b475e2712388 100644 --- a/src/views/endpoint/autopilot/AutopilotAddDevice.jsx +++ b/src/views/endpoint/autopilot/AutopilotAddDevice.jsx @@ -34,6 +34,28 @@ Error.propTypes = { const AddAPDevice = () => { const [genericPostRequest, postResults] = useLazyGenericPostRequestQuery() const [autopilotData, setAutopilotdata] = useState([]) + const completeColumns = [ + { + name: 'Serial Number', + selector: (row) => row['serialNumber'], + sortable: true, + }, + { + name: 'Status', + selector: (row) => row['status'], + sortable: true, + }, + { + name: 'Error Code', + selector: (row) => row['errorCode'], + sortable: true, + }, + { + name: 'Error Description', + selector: (row) => row['errorDescription'], + sortable: true, + }, + ] const tableColumns = [ { name: 'serialNumber', @@ -267,8 +289,18 @@ const AddAPDevice = () => { Loading )} - {postResults.isSuccess && {postResults.data.Results}} - {autopilotData && ( + {postResults.isSuccess && ( + <> + {postResults.data?.Results?.Status} + + + )} + {autopilotData && !postResults.isSuccess && ( Date: Thu, 30 May 2024 18:40:12 +0200 Subject: [PATCH 229/475] fixes bpa if never run --- .../tenant/standards/BestPracticeAnalyser.jsx | 198 ++++++++++-------- 1 file changed, 107 insertions(+), 91 deletions(-) diff --git a/src/views/tenant/standards/BestPracticeAnalyser.jsx b/src/views/tenant/standards/BestPracticeAnalyser.jsx index 10e7c480c29f..801cd1eb6838 100644 --- a/src/views/tenant/standards/BestPracticeAnalyser.jsx +++ b/src/views/tenant/standards/BestPracticeAnalyser.jsx @@ -346,99 +346,115 @@ const BestPracticeAnalyser = () => { refreshFunction={setRefreshValue} />
    - {graphrequest.data.Columns.map((info, idx) => ( - - - - {info.name} - - - - {info.formatter === 'bool' && ( - - - {graphrequest.data.Data[0][info.value] ? 'Yes' : 'No'} - - )} - {info.formatter === 'reverseBool' && ( - - - {graphrequest.data.Data[0][info.value] ? 'No' : 'Yes'} - - )} - {info.formatter === 'warnBool' && ( - - - {graphrequest.data.Data[0][info.value] ? 'Yes' : 'No'} - - )} + {graphrequest.data?.Data[0] && + Object.keys(graphrequest.data.Data[0]).length === 0 ? ( + + + Best Practice Report + + + + No Data Found for this tenant. Please refresh the tenant data. + + + + ) : ( + graphrequest.data.Columns.map((info, idx) => ( + + + + {info.name} + + + + {info.formatter === 'bool' && ( + + + {graphrequest.data.Data[0][info.value] ? 'Yes' : 'No'} + + )} + {info.formatter === 'reverseBool' && ( + + + {graphrequest.data.Data[0][info.value] ? 'No' : 'Yes'} + + )} + {info.formatter === 'warnBool' && ( + + + {graphrequest.data.Data[0][info.value] ? 'Yes' : 'No'} + + )} - {info.formatter === 'table' && ( - <> - - - )} + {info.formatter === 'table' && ( + <> + + + )} - {info.formatter === 'number' && ( -

    - {getNestedValue(graphrequest.data.Data[0], info.value)} -

    - )} -
    - - {info.desc} - -
    -
    -
    - ))} + {info.formatter === 'number' && ( +

    + {getNestedValue(graphrequest.data.Data[0], info.value)} +

    + )} +
    + + {info.desc} + +
    +
    +
    + )) + )} )} From 472d523038ec66e2d290ae779089994e39037fc4 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Fri, 31 May 2024 20:42:32 +0200 Subject: [PATCH 230/475] fixes errormessages not showing up --- src/views/cipp/app-settings/SettingsGeneral.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/cipp/app-settings/SettingsGeneral.jsx b/src/views/cipp/app-settings/SettingsGeneral.jsx index 04446add8ceb..346f1ac3fe53 100644 --- a/src/views/cipp/app-settings/SettingsGeneral.jsx +++ b/src/views/cipp/app-settings/SettingsGeneral.jsx @@ -253,7 +253,7 @@ export function SettingsGeneral() { - {permissionsResult.data.Results?.ErrorMessages?.length > 0 || + {permissionsResult.data.Results?.ErrorMessages?.length >= 0 || (permissionsResult.data.Results?.MissingPermissions.length > 0 && ( <> From 01dc70a15eae4250304f5c6c70fb43c3a59cdb5b Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Fri, 31 May 2024 21:07:59 +0200 Subject: [PATCH 231/475] update settings --- src/views/cipp/app-settings/SettingsGeneral.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/cipp/app-settings/SettingsGeneral.jsx b/src/views/cipp/app-settings/SettingsGeneral.jsx index 346f1ac3fe53..bb0d16b4946b 100644 --- a/src/views/cipp/app-settings/SettingsGeneral.jsx +++ b/src/views/cipp/app-settings/SettingsGeneral.jsx @@ -253,7 +253,7 @@ export function SettingsGeneral() { - {permissionsResult.data.Results?.ErrorMessages?.length >= 0 || + {permissionsResult.data.Results?.ErrorMessages?.length || (permissionsResult.data.Results?.MissingPermissions.length > 0 && ( <> From 8da455d400c247574ee2d7df69c6043af953d4d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Kj=C3=A6rg=C3=A5rd?= Date: Fri, 31 May 2024 21:13:47 +0200 Subject: [PATCH 232/475] Add TeamsMeetingsByDefault standard --- src/data/standards.json | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/data/standards.json b/src/data/standards.json index 3efbc09ea197..e1cb16aadaf7 100644 --- a/src/data/standards.json +++ b/src/data/standards.json @@ -696,6 +696,32 @@ "impact": "Low Impact", "impactColour": "info" }, + { + "name": "standards.TeamsMeetingsByDefault", + "cat": "Exchange Standards", + "tag": ["lowimpact"], + "helpText": "Sets the default state for automatically turning meetings into Teams meetings for the tenant. This can be overridden by the user in Outlook.", + "addedComponent": [ + { + "type": "Select", + "label": "Select value", + "name": "standards.TeamsMeetingsByDefault.state", + "values": [ + { + "label": "Enabled", + "value": "true" + }, + { + "label": "Disabled", + "value": "false" + } + ] + } + ], + "label": "Set Teams Meetings by default state", + "impact": "Low Impact", + "impactColour": "info" + }, { "name": "standards.DisableViva", "cat": "Exchange Standards", From 180f176fe6f1e55692de09a044973d88aaf71ad2 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Fri, 31 May 2024 21:20:03 +0200 Subject: [PATCH 233/475] permissions fix --- .../cipp/app-settings/SettingsGeneral.jsx | 58 +++++++++---------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/src/views/cipp/app-settings/SettingsGeneral.jsx b/src/views/cipp/app-settings/SettingsGeneral.jsx index bb0d16b4946b..fc5472998bc8 100644 --- a/src/views/cipp/app-settings/SettingsGeneral.jsx +++ b/src/views/cipp/app-settings/SettingsGeneral.jsx @@ -253,37 +253,37 @@ export function SettingsGeneral() { - {permissionsResult.data.Results?.ErrorMessages?.length || - (permissionsResult.data.Results?.MissingPermissions.length > 0 && ( - + {(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 && ( <> - {permissionsResult.data.Results?.ErrorMessages?.map((m, idx) => ( -
    {m}
    - ))} + 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} - ), - )} - - - )} -
    - ))} + )} +
    + )}
    From 900c00705b2d58c5935a08ea0425464f5c5e3d3f Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Fri, 31 May 2024 21:29:22 +0200 Subject: [PATCH 234/475] fixed --- src/views/cipp/app-settings/SettingsGeneral.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/cipp/app-settings/SettingsGeneral.jsx b/src/views/cipp/app-settings/SettingsGeneral.jsx index fc5472998bc8..b5dd40624064 100644 --- a/src/views/cipp/app-settings/SettingsGeneral.jsx +++ b/src/views/cipp/app-settings/SettingsGeneral.jsx @@ -253,7 +253,7 @@ export function SettingsGeneral() { - {(permissionsResult.data.Results?.ErrorMessages?.length >= 0 || + {(permissionsResult.data.Results?.ErrorMessages?.length > 0 || permissionsResult.data.Results?.MissingPermissions.length > 0) && ( <> From e7738cfc584b366a5fc6950539058315adf0c382 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Kj=C3=A6rg=C3=A5rd?= Date: Fri, 31 May 2024 21:32:03 +0200 Subject: [PATCH 235/475] Add bookings standard --- src/data/standards.json | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/data/standards.json b/src/data/standards.json index 3efbc09ea197..a626c9692567 100644 --- a/src/data/standards.json +++ b/src/data/standards.json @@ -872,6 +872,32 @@ "impact": "Medium Impact", "impactColour": "warning" }, + { + "name": "standards.Bookings", + "cat": "Exchange Standards", + "tag": ["mediumimpact"], + "helpText": "Sets the state of Bookings on the tenant. Bookings is a scheduling tool that allows users to book appointments with others both internal and external.", + "addedComponent": [ + { + "type": "Select", + "label": "Select value", + "name": "standards.Bookings.state", + "values": [ + { + "label": "Enabled", + "value": "true" + }, + { + "label": "Disabled", + "value": "false" + } + ] + } + ], + "label": "Set Bookings state", + "impact": "Medium Impact", + "impactColour": "warning" + }, { "name": "standards.DisableOutlookAddins", "cat": "Exchange Standards", From 881232f959f82333ffd333f11c28b2df17134217 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Fri, 31 May 2024 20:42:32 +0200 Subject: [PATCH 236/475] fixes errormessages not showing up --- src/views/cipp/app-settings/SettingsGeneral.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/cipp/app-settings/SettingsGeneral.jsx b/src/views/cipp/app-settings/SettingsGeneral.jsx index 04446add8ceb..346f1ac3fe53 100644 --- a/src/views/cipp/app-settings/SettingsGeneral.jsx +++ b/src/views/cipp/app-settings/SettingsGeneral.jsx @@ -253,7 +253,7 @@ export function SettingsGeneral() { - {permissionsResult.data.Results?.ErrorMessages?.length > 0 || + {permissionsResult.data.Results?.ErrorMessages?.length >= 0 || (permissionsResult.data.Results?.MissingPermissions.length > 0 && ( <> From 808fbe59e9f29ab630fa1613cd87c5a8c8aeaeb5 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Fri, 31 May 2024 21:07:59 +0200 Subject: [PATCH 237/475] update settings --- src/views/cipp/app-settings/SettingsGeneral.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/cipp/app-settings/SettingsGeneral.jsx b/src/views/cipp/app-settings/SettingsGeneral.jsx index 346f1ac3fe53..bb0d16b4946b 100644 --- a/src/views/cipp/app-settings/SettingsGeneral.jsx +++ b/src/views/cipp/app-settings/SettingsGeneral.jsx @@ -253,7 +253,7 @@ export function SettingsGeneral() { - {permissionsResult.data.Results?.ErrorMessages?.length >= 0 || + {permissionsResult.data.Results?.ErrorMessages?.length || (permissionsResult.data.Results?.MissingPermissions.length > 0 && ( <> From af3accaa2a7e38954154b7f6162768d481b5b71d Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Fri, 31 May 2024 21:20:03 +0200 Subject: [PATCH 238/475] permissions fix --- .../cipp/app-settings/SettingsGeneral.jsx | 58 +++++++++---------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/src/views/cipp/app-settings/SettingsGeneral.jsx b/src/views/cipp/app-settings/SettingsGeneral.jsx index bb0d16b4946b..fc5472998bc8 100644 --- a/src/views/cipp/app-settings/SettingsGeneral.jsx +++ b/src/views/cipp/app-settings/SettingsGeneral.jsx @@ -253,37 +253,37 @@ export function SettingsGeneral() { - {permissionsResult.data.Results?.ErrorMessages?.length || - (permissionsResult.data.Results?.MissingPermissions.length > 0 && ( - + {(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 && ( <> - {permissionsResult.data.Results?.ErrorMessages?.map((m, idx) => ( -
    {m}
    - ))} + 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} - ), - )} - - - )} -
    - ))} + )} +
    + )}
    From 22e0be1c2aab72382f5b201d3f5db6937168c2ea Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Fri, 31 May 2024 21:29:22 +0200 Subject: [PATCH 239/475] fixed --- src/views/cipp/app-settings/SettingsGeneral.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/cipp/app-settings/SettingsGeneral.jsx b/src/views/cipp/app-settings/SettingsGeneral.jsx index fc5472998bc8..b5dd40624064 100644 --- a/src/views/cipp/app-settings/SettingsGeneral.jsx +++ b/src/views/cipp/app-settings/SettingsGeneral.jsx @@ -253,7 +253,7 @@ export function SettingsGeneral() { - {(permissionsResult.data.Results?.ErrorMessages?.length >= 0 || + {(permissionsResult.data.Results?.ErrorMessages?.length > 0 || permissionsResult.data.Results?.MissingPermissions.length > 0) && ( <> From ffdc7444b6bed4a1fba68fd2ed587c9f94917533 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Sun, 2 Jun 2024 12:54:01 -0400 Subject: [PATCH 240/475] Fix JIT route --- src/importsMap.jsx | 2 +- .../administration/DeployJITAdmin.jsx | 20 ++++--------------- 2 files changed, 5 insertions(+), 17 deletions(-) diff --git a/src/importsMap.jsx b/src/importsMap.jsx index d42693788afb..b50612327848 100644 --- a/src/importsMap.jsx +++ b/src/importsMap.jsx @@ -12,7 +12,7 @@ import React from 'react' "/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/users/jit-admin": React.lazy(() => import('./views/identity/administration/DeployJITAdmin')), + "/identity/administration/jit-admin": React.lazy(() => import('./views/identity/administration/DeployJITAdmin')), "/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')), diff --git a/src/views/identity/administration/DeployJITAdmin.jsx b/src/views/identity/administration/DeployJITAdmin.jsx index 126150f95360..0bd5af20df75 100644 --- a/src/views/identity/administration/DeployJITAdmin.jsx +++ b/src/views/identity/administration/DeployJITAdmin.jsx @@ -1,31 +1,19 @@ import React, { useState } from 'react' import { CButton, CCallout, CCol, CForm, CRow, CSpinner, CTooltip } from '@coreui/react' import { useSelector } from 'react-redux' -import { Field, Form, FormSpy } from 'react-final-form' -import { - Condition, - RFFCFormInput, - RFFCFormRadio, - RFFCFormRadioList, - RFFCFormSwitch, - RFFSelectSearch, -} from 'src/components/forms' -import { - useGenericGetRequestQuery, - useLazyGenericGetRequestQuery, - useLazyGenericPostRequestQuery, -} from 'src/store/api/app' +import { Field, Form } from 'react-final-form' +import { Condition, RFFCFormInput, RFFCFormRadioList, RFFSelectSearch } from 'src/components/forms' +import { useLazyGenericGetRequestQuery, useLazyGenericPostRequestQuery } from 'src/store/api/app' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { faCircleNotch, faEdit, faEye } from '@fortawesome/free-solid-svg-icons' import { CippContentCard, CippPage, CippPageList } from 'src/components/layout' import { CellTip } from 'src/components/tables/CellGenericFormat' import 'react-datepicker/dist/react-datepicker.css' -import { CippActionsOffcanvas, ModalService, TenantSelector } from 'src/components/utilities' +import { TenantSelector } from 'src/components/utilities' import arrayMutators from 'final-form-arrays' import DatePicker from 'react-datepicker' import 'react-datepicker/dist/react-datepicker.css' import { useListUsersQuery } from 'src/store/api/users' -import { useListConditionalAccessPoliciesQuery } from 'src/store/api/tenants' import GDAPRoles from 'src/data/GDAPRoles' const DeployJITAdmin = () => { From 5ede28816b0d8b0fd3a266ed9c47b740224179b4 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Mon, 3 Jun 2024 13:06:12 -0400 Subject: [PATCH 241/475] JIT Admin --- package.json | 2 +- public/version_latest.txt | 2 +- .../administration/DeployJITAdmin.jsx | 70 ++++++++++++++++--- version_latest.txt | 2 +- 4 files changed, 65 insertions(+), 11 deletions(-) diff --git a/package.json b/package.json index 332ac7504f6e..76ec78f43af2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cipp", - "version": "5.7.0", + "version": "5.7.1", "description": "The CyberDrain Improved Partner Portal is a portal to help manage administration for Microsoft Partners.", "homepage": "https://cipp.app/", "bugs": { diff --git a/public/version_latest.txt b/public/version_latest.txt index 42cdd0b540f9..64ff7ded70d3 100644 --- a/public/version_latest.txt +++ b/public/version_latest.txt @@ -1 +1 @@ -5.7.0 +5.7.1 diff --git a/src/views/identity/administration/DeployJITAdmin.jsx b/src/views/identity/administration/DeployJITAdmin.jsx index 0bd5af20df75..40a5f88c4a9e 100644 --- a/src/views/identity/administration/DeployJITAdmin.jsx +++ b/src/views/identity/administration/DeployJITAdmin.jsx @@ -7,7 +7,7 @@ import { useLazyGenericGetRequestQuery, useLazyGenericPostRequestQuery } from 's import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { faCircleNotch, faEdit, faEye } from '@fortawesome/free-solid-svg-icons' import { CippContentCard, CippPage, CippPageList } from 'src/components/layout' -import { CellTip } from 'src/components/tables/CellGenericFormat' +import { CellTip, cellGenericFormatter } from 'src/components/tables/CellGenericFormat' import 'react-datepicker/dist/react-datepicker.css' import { TenantSelector } from 'src/components/utilities' import arrayMutators from 'final-form-arrays' @@ -15,6 +15,7 @@ import DatePicker from 'react-datepicker' import 'react-datepicker/dist/react-datepicker.css' import { useListUsersQuery } from 'src/store/api/users' import GDAPRoles from 'src/data/GDAPRoles' +import { CippDatatable, cellDateFormatter } from 'src/components/tables' const DeployJITAdmin = () => { const [ExecuteGetRequest, getResults] = useLazyGenericGetRequestQuery() @@ -30,12 +31,13 @@ const DeployJITAdmin = () => { const startTime = Math.floor(startDate.getTime() / 1000) const endTime = Math.floor(endDate.getTime() / 1000) const shippedValues = { - tenantFilter: tenantDomain, + TenantFilter: tenantDomain, UserId: values.UserId?.value, - PolicyId: values.PolicyId?.value, + useraction: values.useraction, + AdminRoles: values.AdminRoles?.map((role) => role.value), StartDate: startTime, EndDate: endTime, - ExpireAction: values?.expireAction ?? 'delete', + ExpireAction: values.expireAction.value, } genericPostRequest({ path: '/api/ExecJITAdmin', values: shippedValues }).then((res) => { setRefreshState(res.requestId) @@ -51,8 +53,8 @@ const DeployJITAdmin = () => { return ( <> - - + + { - {(props) => } + + {(props) => } + @@ -90,7 +94,17 @@ const DeployJITAdmin = () => { - + + + + + + + + + + + @@ -112,6 +126,7 @@ const DeployJITAdmin = () => { +
    { /> + + + row['userPrincipalName'], + sortable: true, + cell: cellGenericFormatter(), + exportSelector: 'userPrincipalName', + }, + { + name: 'JIT Enabled', + selector: (row) => row['jitAdminEnabled'], + sortable: true, + cell: cellGenericFormatter(), + exportSelector: 'jitAdminEnabled', + }, + { + name: 'JIT Expires', + selector: (row) => row['jitAdminExpiration'], + sortable: true, + cell: cellDateFormatter({ format: 'short' }), + exportSelector: 'jitAdminExpiration', + }, + { + name: 'Admin Roles', + selector: (row) => row['memberOf'], + sortable: false, + cell: cellGenericFormatter(), + exportSelector: 'memberOf', + }, + ]} + /> + +
    diff --git a/version_latest.txt b/version_latest.txt index 42cdd0b540f9..64ff7ded70d3 100644 --- a/version_latest.txt +++ b/version_latest.txt @@ -1 +1 @@ -5.7.0 +5.7.1 From b542489ecae0e53f3bf904ed2fe466f3c86c5885 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Mon, 3 Jun 2024 15:18:08 -0400 Subject: [PATCH 242/475] Update JIT actions --- src/views/identity/administration/DeployJITAdmin.jsx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/views/identity/administration/DeployJITAdmin.jsx b/src/views/identity/administration/DeployJITAdmin.jsx index 40a5f88c4a9e..aad2244ba34f 100644 --- a/src/views/identity/administration/DeployJITAdmin.jsx +++ b/src/views/identity/administration/DeployJITAdmin.jsx @@ -172,9 +172,9 @@ const DeployJITAdmin = () => { { {postResults.isSuccess && ( -
  • {postResults.data.Results}
  • + {postResults.data?.Results.map((result, idx) => ( +
  • {result}
  • + ))}
    )} {getResults.isFetching && ( From c889ed1dacaf33aa1ac45307dac57b403630eb4f Mon Sep 17 00:00:00 2001 From: John Duprey Date: Mon, 3 Jun 2024 20:39:20 -0400 Subject: [PATCH 243/475] Update DeployJITAdmin.jsx --- .../administration/DeployJITAdmin.jsx | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/src/views/identity/administration/DeployJITAdmin.jsx b/src/views/identity/administration/DeployJITAdmin.jsx index aad2244ba34f..b87ca67dd8f3 100644 --- a/src/views/identity/administration/DeployJITAdmin.jsx +++ b/src/views/identity/administration/DeployJITAdmin.jsx @@ -2,7 +2,13 @@ import React, { useState } from 'react' import { CButton, CCallout, CCol, CForm, CRow, CSpinner, CTooltip } from '@coreui/react' import { useSelector } from 'react-redux' import { Field, Form } from 'react-final-form' -import { Condition, RFFCFormInput, RFFCFormRadioList, RFFSelectSearch } from 'src/components/forms' +import { + Condition, + RFFCFormInput, + RFFCFormRadioList, + RFFCFormSwitch, + RFFSelectSearch, +} from 'src/components/forms' import { useLazyGenericGetRequestQuery, useLazyGenericPostRequestQuery } from 'src/store/api/app' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { faCircleNotch, faEdit, faEye } from '@fortawesome/free-solid-svg-icons' @@ -33,11 +39,19 @@ const DeployJITAdmin = () => { const shippedValues = { TenantFilter: tenantDomain, UserId: values.UserId?.value, + UserPrincipalName: values.UserPrincipalName, + FirstName: values.FirstName, + LastName: values.LastName, useraction: values.useraction, AdminRoles: values.AdminRoles?.map((role) => role.value), StartDate: startTime, EndDate: endTime, ExpireAction: values.expireAction.value, + PostExecution: { + Webhook: values.webhook, + Email: values.email, + PSA: values.psa, + }, } genericPostRequest({ path: '/api/ExecJITAdmin', values: shippedValues }).then((res) => { setRefreshState(res.requestId) @@ -181,6 +195,14 @@ const DeployJITAdmin = () => { />
    + + + + + + + + @@ -227,7 +249,7 @@ const DeployJITAdmin = () => { Date: Wed, 5 Jun 2024 11:48:23 +0200 Subject: [PATCH 244/475] Added more Anti-Phishing actions --- src/data/standards.json | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/src/data/standards.json b/src/data/standards.json index d1b9e020ded7..2f9cbb7465c1 100644 --- a/src/data/standards.json +++ b/src/data/standards.json @@ -1102,6 +1102,44 @@ "name": "standards.AntiPhishPolicy.EnableUnusualCharactersSafetyTips", "default": true }, + { + "type": "Select", + "label": "If a message is detected as user impersonation", + "name": "standards.AntiPhishPolicy.TargetedUserProtectionAction", + "values": [ + { + "label": "Move to Junk Folder", + "value": "MoveToJmf" + }, + { + "label": "Delete the message before its delivered", + "value": "Delete" + }, + { + "label": "Quarantine the message", + "value": "Quarantine" + } + ] + }, + { + "type": "Select", + "label": "If a message is detected as domain impersonation", + "name": "standards.AntiPhishPolicy.TargetedDomainProtectionAction", + "values": [ + { + "label": "Move to Junk Folder", + "value": "MoveToJmf" + }, + { + "label": "Delete the message before its delivered", + "value": "Delete" + }, + { + "label": "Quarantine the message", + "value": "Quarantine" + } + ] + }, { "type": "Select", "label": "If Mailbox Intelligence detects an impersonated user", From 651de591942fb4a4dedac1e340a3db4ca8c2d27c Mon Sep 17 00:00:00 2001 From: John Duprey Date: Wed, 5 Jun 2024 09:00:47 -0400 Subject: [PATCH 245/475] Tenant block list --- .../components/SettingsCustomRoles.jsx | 55 +++++++++++++++++-- 1 file changed, 50 insertions(+), 5 deletions(-) diff --git a/src/views/cipp/app-settings/components/SettingsCustomRoles.jsx b/src/views/cipp/app-settings/components/SettingsCustomRoles.jsx index 75c0a7504487..32e2ef9d6511 100644 --- a/src/views/cipp/app-settings/components/SettingsCustomRoles.jsx +++ b/src/views/cipp/app-settings/components/SettingsCustomRoles.jsx @@ -13,20 +13,20 @@ import { import { Field, Form, FormSpy } from 'react-final-form' import { RFFCFormRadioList, RFFSelectSearch } from 'src/components/forms' import { useGenericGetRequestQuery, useLazyGenericPostRequestQuery } from 'src/store/api/app' -import { CippPage } from 'src/components/layout' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' -import Skeleton from 'react-loading-skeleton' import { TenantSelectorMultiple, ModalService, CippOffcanvas } from 'src/components/utilities' import PropTypes from 'prop-types' import { OnChange } from 'react-final-form-listeners' import { useListTenantsQuery } from 'src/store/api/tenants' -import CippListOffcanvas, { OffcanvasListSection } from 'src/components/utilities/CippListOffcanvas' +import { OffcanvasListSection } from 'src/components/utilities/CippListOffcanvas' import CippButtonCard from 'src/components/contentcards/CippButtonCard' const SettingsCustomRoles = () => { const [genericPostRequest, postResults] = useLazyGenericPostRequestQuery() const [selectedTenant, setSelectedTenant] = useState([]) + const [blockedTenants, setBlockedTenants] = useState([]) const tenantSelectorRef = useRef() + const blockedTenantSelectorRef = useRef() const { data: tenants = [], tenantsFetching } = useListTenantsQuery({ showAllTenantSelector: true, }) @@ -57,13 +57,21 @@ const SettingsCustomRoles = () => { alltenant = true } }) - if (alltenant) { + if (alltenant && blockedTenants.length === 0) { setAllTenantSelected(true) } else { setAllTenantSelected(false) } setSelectedTenant(e) } + + const handleBlockedTenantChange = (e) => { + setBlockedTenants(e) + if (e.length > 0) { + setAllTenantSelected(false) + } + } + const handleSubmit = async (values) => { //filter on only objects that are 'true' genericPostRequest({ @@ -72,6 +80,7 @@ const SettingsCustomRoles = () => { RoleName: values.RoleName.value, Permissions: values.Permissions, AllowedTenants: selectedTenant.map((tenant) => tenant.value), + BlockedTenants: blockedTenants.map((tenant) => tenant.value), }, }).then(() => { refetchCustomRoleList() @@ -130,6 +139,19 @@ const SettingsCustomRoles = () => { }) tenantSelectorRef.current.setValue(selectedTenants) + } else if (set === 'BlockedTenants') { + setBlockedTenants(customRole[0][set]) + var blockedTenants = [] + tenants.map((tenant) => { + if (customRole[0][set].includes(tenant.customerId)) { + blockedTenants.push({ + label: tenant.displayName, + value: tenant.customerId, + }) + } + }) + + blockedTenantSelectorRef.current.setValue(blockedTenants) } else { onChange(customRole[0][set]) } @@ -277,6 +299,7 @@ const SettingsCustomRoles = () => { /> + {cippApiRoleSelected && ( This role will limit access for the CIPP-API integration. It is not @@ -299,6 +322,18 @@ const SettingsCustomRoles = () => { )}
    + {selectedTenant.find((tenant) => tenant.value === 'AllTenants') && ( +
    +
    Blocked Tenants
    + handleBlockedTenantChange(e)} + /> +
    + )}
    API Permissions
    @@ -366,7 +401,7 @@ const SettingsCustomRoles = () => { <> {values['RoleName'] && selectedTenant.length > 0 && ( <> -
    Selected Tenants
    +
    Allowed Tenants
      {selectedTenant.map((tenant, idx) => (
    • {tenant.label}
    • @@ -374,6 +409,16 @@ const SettingsCustomRoles = () => {
    )} + {values['RoleName'] && blockedTenants.length > 0 && ( + <> +
    Blocked Tenants
    +
      + {blockedTenants.map((tenant, idx) => ( +
    • {tenant.label}
    • + ))} +
    + + )} {values['RoleName'] && values['Permissions'] && ( <>
    Selected Permissions
    From af8202c6118e2c99ad18c05f820b6104e45b6f37 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Wed, 5 Jun 2024 09:15:32 -0400 Subject: [PATCH 246/475] Add sort --- .../components/SettingsCustomRoles.jsx | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/views/cipp/app-settings/components/SettingsCustomRoles.jsx b/src/views/cipp/app-settings/components/SettingsCustomRoles.jsx index 32e2ef9d6511..f9d0bac3b359 100644 --- a/src/views/cipp/app-settings/components/SettingsCustomRoles.jsx +++ b/src/views/cipp/app-settings/components/SettingsCustomRoles.jsx @@ -424,13 +424,15 @@ const SettingsCustomRoles = () => {
    Selected Permissions
      {values['Permissions'] && - Object.keys(values['Permissions'])?.map((cat, idx) => ( - <> - {!values['Permissions'][cat].includes('None') && ( -
    • {values['Permissions'][cat]}
    • - )} - - ))} + Object.keys(values['Permissions']) + ?.sort() + .map((cat, idx) => ( + <> + {!values['Permissions'][cat].includes('None') && ( +
    • {values['Permissions'][cat]}
    • + )} + + ))}
    )} From 8b359388a022ef04616e1b4e5921e6127b15b6d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Kj=C3=A6rg=C3=A5rd?= Date: Wed, 5 Jun 2024 15:23:58 +0200 Subject: [PATCH 247/475] Add Pronouns standard --- src/data/standards.json | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/data/standards.json b/src/data/standards.json index d1b9e020ded7..ed45971bc7fe 100644 --- a/src/data/standards.json +++ b/src/data/standards.json @@ -121,6 +121,16 @@ "impact": "Low Impact", "impactColour": "info" }, + { + "name": "standards.EnablePronouns", + "cat": "Global Standards", + "tag": ["lowimpact"], + "helpText": "Enables the Pronouns feature for the tenant. This allows users to set their pronouns in their profile.", + "addedComponent": [], + "label": "Enable Pronouns", + "impact": "Low Impact", + "impactColour": "info" + }, { "name": "standards.AnonReportDisable", "cat": "Global Standards", From 8a979de71eefa7af04c061d35a09a3752bc8244e Mon Sep 17 00:00:00 2001 From: John Duprey Date: Wed, 5 Jun 2024 09:50:34 -0400 Subject: [PATCH 248/475] Update SettingsCustomRoles.jsx --- .../components/SettingsCustomRoles.jsx | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/views/cipp/app-settings/components/SettingsCustomRoles.jsx b/src/views/cipp/app-settings/components/SettingsCustomRoles.jsx index f9d0bac3b359..1d7d08426946 100644 --- a/src/views/cipp/app-settings/components/SettingsCustomRoles.jsx +++ b/src/views/cipp/app-settings/components/SettingsCustomRoles.jsx @@ -128,30 +128,31 @@ const SettingsCustomRoles = () => { } else { if (set === 'AllowedTenants') { setSelectedTenant(customRole[0][set]) - var selectedTenants = [] + var selectedTenantList = [] tenants.map((tenant) => { if (customRole[0][set].includes(tenant.customerId)) { - selectedTenants.push({ + selectedTenantList.push({ label: tenant.displayName, value: tenant.customerId, }) } }) - tenantSelectorRef.current.setValue(selectedTenants) + tenantSelectorRef.current.setValue(selectedTenantList) } else if (set === 'BlockedTenants') { setBlockedTenants(customRole[0][set]) - var blockedTenants = [] + var blockedTenantList = [] tenants.map((tenant) => { if (customRole[0][set].includes(tenant.customerId)) { - blockedTenants.push({ + blockedTenantList.push({ label: tenant.displayName, value: tenant.customerId, }) } }) - - blockedTenantSelectorRef.current.setValue(blockedTenants) + if (blockedTenantSelectorRef?.current) { + blockedTenantSelectorRef.current.setValue(blockedTenantList) + } } else { onChange(customRole[0][set]) } From e742061e21a8138f6a1720732b5213bb743467f4 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Wed, 5 Jun 2024 10:42:38 -0400 Subject: [PATCH 249/475] Add input list for string[] values --- src/components/forms/RFFComponents.jsx | 48 ++++++++++++++++++++++++++ src/components/forms/index.js | 2 ++ src/views/cipp/Scheduler.jsx | 39 +++++++++++++++------ 3 files changed, 79 insertions(+), 10 deletions(-) diff --git a/src/components/forms/RFFComponents.jsx b/src/components/forms/RFFComponents.jsx index b72a840d4989..292187a957db 100644 --- a/src/components/forms/RFFComponents.jsx +++ b/src/components/forms/RFFComponents.jsx @@ -253,6 +253,54 @@ RFFCFormInputArray.propTypes = { ...sharedPropTypes, } +export const RFFCFormInputList = ({ name, label, className = 'mb-3' }) => { + return ( + <> + + {({ fields }) => ( +
    +
    + {label && ( + + {label} + + )} + fields.push({ Key: '', Value: '' })} + className="circular-button" + title={'+'} + > + + +
    + {fields.map((name, index) => ( +
    +
    + + {({ input, meta }) => { + return + }} + +
    + fields.remove(index)} + className={`circular-button`} + title={'-'} + > + + +
    + ))} +
    + )} +
    + + ) +} +RFFCFormInputList.propTypes = { + ...sharedPropTypes, +} + export const RFFCFormRadio = ({ name, label, diff --git a/src/components/forms/index.js b/src/components/forms/index.js index 00a76ee4554a..aa36929d58da 100644 --- a/src/components/forms/index.js +++ b/src/components/forms/index.js @@ -10,6 +10,7 @@ import { RFFCFormSelect, RFFSelectSearch, RFFCFormInputArray, + RFFCFormInputList, } from 'src/components/forms/RFFComponents' export { @@ -24,4 +25,5 @@ export { RFFCFormSelect, RFFSelectSearch, RFFCFormInputArray, + RFFCFormInputList, } diff --git a/src/views/cipp/Scheduler.jsx b/src/views/cipp/Scheduler.jsx index 7ffed5837fe0..e0fd1aea974a 100644 --- a/src/views/cipp/Scheduler.jsx +++ b/src/views/cipp/Scheduler.jsx @@ -5,6 +5,7 @@ import { Field, Form, FormSpy } from 'react-final-form' import { RFFCFormInput, RFFCFormInputArray, + RFFCFormInputList, RFFCFormSwitch, RFFSelectSearch, } from 'src/components/forms' @@ -157,10 +158,14 @@ const Scheduler = () => { if (typeof row?.Parameters[key] === 'object') { var nestedParamList = [] Object.keys(row?.Parameters[key]).forEach((nestedKey) => { - nestedParamList.push({ - Key: nestedKey, - Value: row?.Parameters[key][nestedKey], - }) + if (nestedKey >= 0) { + nestedParamList.push(row?.Parameters[key][nestedKey]) + } else { + nestedParamList.push({ + Key: nestedKey, + Value: row?.Parameters[key][nestedKey], + }) + } }) parameters[key] = nestedParamList } else { @@ -179,6 +184,10 @@ const Scheduler = () => { }) }) + if (!recurrence) { + recurrence = { name: 'Only once', value: '0' } + } + // Set initial values var formValues = { taskName: row.Name, @@ -413,12 +422,22 @@ const Scheduler = () => { key={idx} /> ) : ( - + <> + {param.Type === 'System.String[]' ? ( + + ) : ( + + )} + )} )} From 665aafeae8e42eb5cdf7e987523cb226bcfeb6f7 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Wed, 5 Jun 2024 11:08:15 -0400 Subject: [PATCH 250/475] Update SettingsCustomRoles.jsx --- .../components/SettingsCustomRoles.jsx | 28 +++++++++---------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/src/views/cipp/app-settings/components/SettingsCustomRoles.jsx b/src/views/cipp/app-settings/components/SettingsCustomRoles.jsx index 1d7d08426946..5799c900323f 100644 --- a/src/views/cipp/app-settings/components/SettingsCustomRoles.jsx +++ b/src/views/cipp/app-settings/components/SettingsCustomRoles.jsx @@ -150,9 +150,8 @@ const SettingsCustomRoles = () => { }) } }) - if (blockedTenantSelectorRef?.current) { - blockedTenantSelectorRef.current.setValue(blockedTenantList) - } + + blockedTenantSelectorRef.current.setValue(blockedTenantList) } else { onChange(customRole[0][set]) } @@ -323,18 +322,17 @@ const SettingsCustomRoles = () => { )}
    - {selectedTenant.find((tenant) => tenant.value === 'AllTenants') && ( -
    -
    Blocked Tenants
    - handleBlockedTenantChange(e)} - /> -
    - )} +
    +
    Blocked Tenants
    + handleBlockedTenantChange(e)} + /> +
    +
    API Permissions
    From 95ee2846f855aac5a30669e26361a84539cc0614 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Wed, 5 Jun 2024 17:28:50 -0400 Subject: [PATCH 251/475] Extension tweaks --- src/data/Extensions.json | 60 ++++++++++++++++++++ src/views/cipp/Extensions.jsx | 26 ++++++++- src/views/cipp/app-settings/CIPPSettings.jsx | 2 - 3 files changed, 83 insertions(+), 5 deletions(-) diff --git a/src/data/Extensions.json b/src/data/Extensions.json index 98a5c9a5a3f2..e67d9784fae2 100644 --- a/src/data/Extensions.json +++ b/src/data/Extensions.json @@ -160,5 +160,65 @@ } ], "mappingRequired": true + }, + { + "name": "PasswordPusher", + "type": "PWPush", + "cat": "Passwords", + "forceSyncButton": false, + "helpText": "This integration allows you to generate password links instead of plain text passwords. Visit https://pwpush.com/ or https://github.com/pglombardo/PasswordPusher for more information.", + "SettingOptions": [ + { + "type": "checkbox", + "name": "PWPush.Enabled", + "label": "Replace generated passwords with PWPush links" + }, + { + "type": "input", + "fieldtype": "text", + "name": "PWPush.BaseUrl", + "label": "PWPush URL", + "placeholder": "Enter your PWPush URL. (default: https://pwpush.com)" + }, + { + "type": "input", + "fieldtype": "text", + "name": "PWPush.EmailAddress", + "label": "PWPush email address", + "placeholder": "Enter your email address for PWPush. (optional)" + }, + { + "type": "input", + "fieldtype": "password", + "name": "PWPush.ApiKey", + "label": "PWPush API Key", + "placeholder": "Enter your PWPush API Key. (optional)" + }, + { + "type": "checkbox", + "name": "PWPush.RetrievalStep", + "label": "Click to retrieve password (recommended)" + }, + { + "type": "input", + "fieldtype": "number", + "name": "PWPush.ExpireAfterDays", + "label": "Expiration in Days", + "placeholder": "Expiration time in days. (optional)" + }, + { + "type": "input", + "fieldtype": "number", + "name": "PWPush.ExpireAfterViews", + "label": "Expiration after views", + "placeholder": "Expiration after views. (optional)" + }, + { + "type": "checkbox", + "name": "PWPush.DeletableByViewer", + "label": "Allow deletion of passwords" + } + ], + "mappingRequired": false } ] diff --git a/src/views/cipp/Extensions.jsx b/src/views/cipp/Extensions.jsx index d42e79d9bb38..7bfe1d7feb86 100644 --- a/src/views/cipp/Extensions.jsx +++ b/src/views/cipp/Extensions.jsx @@ -10,7 +10,7 @@ import { CTabContent, CTabPane, } from '@coreui/react' -import { CippPage } from 'src/components/layout' +import { CippCallout, CippPage } from 'src/components/layout' import { CippLazy } from 'src/components/utilities' import { useNavigate } from 'react-router-dom' import useQuery from 'src/hooks/useQuery.jsx' @@ -104,7 +104,7 @@ export default function CIPPExtensions() { - + + {extensionConfigResult?.data?.Results && ( + + {extensionConfigResult?.data?.Results} + + )} + {listExtensionTestResult?.data?.Results && ( + + {listExtensionTestResult?.data?.Results} + {listExtensionTestResult?.data?.Link && ( + + Link + + )} + + )} - + diff --git a/src/views/cipp/app-settings/CIPPSettings.jsx b/src/views/cipp/app-settings/CIPPSettings.jsx index 14e5c3e0cb9e..5a610170e49b 100644 --- a/src/views/cipp/app-settings/CIPPSettings.jsx +++ b/src/views/cipp/app-settings/CIPPSettings.jsx @@ -8,9 +8,7 @@ import { SettingsTenants } from 'src/views/cipp/app-settings/SettingsTenants.jsx import { SettingsBackend } from 'src/views/cipp/app-settings/SettingsBackend.jsx' import { SettingsNotifications } from 'src/views/cipp/app-settings/SettingsNotifications.jsx' import { SettingsLicenses } from 'src/views/cipp/app-settings/SettingsLicenses.jsx' -import { SettingsExtensions } from 'src/views/cipp/app-settings/SettingsExtensions.jsx' import { SettingsMaintenance } from 'src/views/cipp/app-settings/SettingsMaintenance.jsx' -import { SettingsExtensionMappings } from 'src/views/cipp/app-settings/SettingsExtensionMappings.jsx' import { SettingsPartner } from 'src/views/cipp/app-settings/SettingsPartner.jsx' import useQuery from 'src/hooks/useQuery.jsx' import { SettingsSuperAdmin } from './SettingsSuperAdmin.jsx' From 362493072207d64c8e88a7022e451cefb8a5e460 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Wed, 5 Jun 2024 17:46:46 -0400 Subject: [PATCH 252/475] Update Extensions.json --- src/data/Extensions.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/data/Extensions.json b/src/data/Extensions.json index e67d9784fae2..1ca3a87b94d7 100644 --- a/src/data/Extensions.json +++ b/src/data/Extensions.json @@ -190,7 +190,7 @@ { "type": "input", "fieldtype": "password", - "name": "PWPush.ApiKey", + "name": "PWPush.APIKey", "label": "PWPush API Key", "placeholder": "Enter your PWPush API Key. (optional)" }, From 8e0710d8be42c7f8bee7a3814f30951116949c17 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Thu, 6 Jun 2024 15:35:37 +0200 Subject: [PATCH 253/475] fixes bug with rffcomponents and input --- src/components/forms/RFFComponents.jsx | 111 +++++++------------------ 1 file changed, 30 insertions(+), 81 deletions(-) diff --git a/src/components/forms/RFFComponents.jsx b/src/components/forms/RFFComponents.jsx index 292187a957db..b2a0f5472619 100644 --- a/src/components/forms/RFFComponents.jsx +++ b/src/components/forms/RFFComponents.jsx @@ -511,10 +511,10 @@ export const RFFSelectSearch = ({ isLoading = false, allowCreate = false, refreshFunction, - props, + ...props }) => { const [inputText, setInputText] = useState('') - const selectSearchvalues = values.map((val) => ({ + const selectSearchValues = values.map((val) => ({ value: val.value, label: val.name, ...val.props, @@ -538,12 +538,30 @@ export const RFFSelectSearch = ({ return ( {({ meta, input }) => { - const handleChange = onChange - ? (e) => { - input.onChange(e) - onChange(e) - } - : input.onChange + const handleChange = (e) => { + if (onChange) { + onChange(e) + } + input.onChange(e) + } + + const selectProps = { + className: 'react-select-container', + classNamePrefix: 'react-select', + ...input, + name, + id: name, + disabled, + options: selectSearchValues, + placeholder, + isMulti: multi, + inputValue: inputText, + isLoading, + onChange: handleChange, + onInputChange: setOnInputChange, + ...props, + } + return (
    @@ -561,79 +579,10 @@ export const RFFSelectSearch = ({ )} - {!allowCreate && onChange && ( - - )} - {allowCreate && onChange && ( - - )} - {allowCreate && !onChange && ( - + {allowCreate ? ( + + ) : ( + + {dropMessage} + +
    + + ) +} + +CippDropzone.propTypes = { + title: PropTypes.string, + onDrop: PropTypes.func.isRequired, + dropMessage: PropTypes.string, + accept: PropTypes.object, + maxFiles: PropTypes.number, +} + +export default CippDropzone diff --git a/src/importsMap.jsx b/src/importsMap.jsx index 21174e32a0f2..f7c2a6e83234 100644 --- a/src/importsMap.jsx +++ b/src/importsMap.jsx @@ -115,6 +115,7 @@ import React from 'react' "/email/tools/mailbox-restore-wizard": React.lazy(() => import('./views/email-exchange/tools/MailboxRestoreWizard')), "/email/tools/mailbox-restores": React.lazy(() => import('./views/email-exchange/tools/MailboxRestores')), "/email/tools/mail-test": React.lazy(() => import('./views/email-exchange/tools/MailTest')), + "/email/tools/message-viewer": React.lazy(() => import('./views/email-exchange/tools/MessageViewer')), "/email/spamfilter/add-template": React.lazy(() => import('./views/email-exchange/spamfilter/AddSpamfilterTemplate')), "/email/administration/edit-mailbox-permissions": React.lazy(() => import('./views/email-exchange/administration/EditMailboxPermissions')), "/email/administration/add-shared-mailbox": React.lazy(() => import('./views/email-exchange/administration/AddSharedMailbox')), diff --git a/src/routes.json b/src/routes.json index c0a996610e41..be956782af6d 100644 --- a/src/routes.json +++ b/src/routes.json @@ -776,6 +776,12 @@ "component": "views/email-exchange/tools/MailTest", "allowedRoles": ["admin", "editor", "readonly"] }, + { + "path": "/email/tools/message-viewer", + "name": "Message Viewer", + "component": "views/email-exchange/tools/MessageViewer", + "allowedRoles": ["admin", "editor", "readonly"] + }, { "path": "/email/spamfilter/add-template", "name": "Add Spamfilter Template", diff --git a/src/views/email-exchange/tools/MessageViewer.jsx b/src/views/email-exchange/tools/MessageViewer.jsx new file mode 100644 index 000000000000..314762758830 --- /dev/null +++ b/src/views/email-exchange/tools/MessageViewer.jsx @@ -0,0 +1,310 @@ +import React, { useCallback, useEffect, useMemo, useState } from 'react' +import PropTypes from 'prop-types' +import { CippPage, CippMasonry, CippMasonryItem, CippContentCard } from 'src/components/layout' +import { parseEml, readEml, GBKUTF8, decode } from 'eml-parse-js' +import { useMediaPredicate } from 'react-media-hook' +import { useSelector } from 'react-redux' +import { CellDate } from 'src/components/tables' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { + CButton, + CCard, + CCardBody, + CCol, + CDropdown, + CDropdownMenu, + CDropdownToggle, + CLink, + CRow, +} from '@coreui/react' +import ReactTimeAgo from 'react-time-ago' +import { CippCodeBlock, ModalService } from 'src/components/utilities' +import DOMPurify from 'dompurify' +import ReactHtmlParser from 'react-html-parser' +import CippDropzone from 'src/components/utilities/CippDropzone' + +const MessageViewer = ({ emlFile }) => { + const [emlContent, setEmlContent] = useState(null) + const [emailSource, setEmailSource] = useState(emlFile) + const [emlError, setEmlError] = useState(false) + const [messageHtml, setMessageHtml] = useState('') + + const getAttachmentIcon = (contentType) => { + if (contentType.includes('image')) { + return 'image' + } else if (contentType.includes('audio')) { + return 'volume-up' + } else if (contentType.includes('video')) { + return 'video' + } else if (contentType.includes('text')) { + return 'file-lines' + } else if (contentType.includes('pdf')) { + return 'file-pdf' + } else if ( + contentType.includes('zip') || + contentType.includes('compressed') || + contentType.includes('tar') || + contentType.includes('gzip') + ) { + return 'file-zipper' + } else if (contentType.includes('msword')) { + return 'file-word' + } else if (contentType.includes('spreadsheet')) { + return 'file-excel' + } else if (contentType.includes('presentation')) { + return 'file-powerpoint' + } else if (contentType.includes('json') || contentType.includes('xml')) { + return 'file-code' + } else if (contentType.includes('rfc822')) { + return 'envelope' + } else { + return 'file' + } + } + + const downloadAttachment = (attachment, newTab = false) => { + if (attachment?.data) { + var contentType = attachment?.contentType?.split(';')[0] ?? 'text/plain' + var fileBytes = attachment.data + var fileName = attachment.name + downloadFileBytes(fileName, fileBytes, contentType, newTab) + } else { + downloadFile(attachment.name, attachment.data64) + } + } + + const downloadFile = (fileName, base64Content, newTab) => { + const link = document.createElement('a') + link.href = `data:application/octet-stream;base64,${base64Content}` + link.download = fileName + link.click() + } + + const downloadFileBytes = (fileName, fileBytes, contentType, newTab = false) => { + const blob = new Blob([fileBytes], { type: contentType ?? 'application/octet-stream' }) + const url = URL.createObjectURL(blob) + const link = document.createElement('a') + if (newTab) { + if (contentType.includes('rfc822')) { + var content = fileBytes + const nestedMessage = + ModalService.open({ + body: nestedMessage, + title: fileName, + size: 'lg', + }) + } else { + const newWindow = window.open() + newWindow.location.href = url + } + } else { + link.href = url + link.download = fileName + link.click() + URL.revokeObjectURL(url) + } + } + + function isValidDate(d) { + return d instanceof Date && !isNaN(d) + } + + const showEmailModal = (emailSource) => { + ModalService.open({ + data: emailSource, + componentType: 'codeblock', + title: 'Email Source', + size: 'lg', + }) + } + + const EmailButtons = (emailSource) => { + return ( + showEmailModal(emailSource)}> + + View Source + + ) + } + + useEffect(() => { + readEml(emailSource, (err, ReadEmlJson) => { + if (err) { + setEmlError(true) + setEmlContent(null) + setMessageHtml(null) + } else { + setEmlContent(ReadEmlJson) + setEmlError(false) + if (ReadEmlJson.html) { + var sanitizedHtml = DOMPurify.sanitize(ReadEmlJson.html) + var parsedHtml = ReactHtmlParser(sanitizedHtml) + setMessageHtml(parsedHtml) + } else { + setMessageHtml(null) + } + } + }) + }, [emailSource, setMessageHtml, setEmailSource, setEmlError, setEmlContent]) + + var buttons = EmailButtons(emailSource) + + return ( + <> + {emlError && ( + + Unable to parse the EML file, email source is displayed below. + + + )} + + {emlContent && ( + <> + + <> + + +
    + + {emlContent?.from?.name} <{emlContent?.from?.email}> +
    + {emlContent?.to?.length > 0 && ( +
    + + To:{' '} + {emlContent?.to?.map((to) => to.name + ' <' + to.email + '>').join(', ')} + +
    + )} + {emlContent?.cc?.length > 0 && ( +
    + + CC:{' '} + {emlContent?.cc?.map((cc) => cc.name + ' <' + cc.email + '>').join(', ')} + +
    + )} +
    + +
    + + + {emlContent.date && isValidDate(emlContent.date) + ? emlContent.date.toLocaleDateString() + : 'Invalid Date'} + + {emlContent.date && isValidDate(emlContent.date) && ( + <> + () + + )} + +
    +
    +
    + + + {emlContent.attachments && emlContent.attachments.length > 0 && ( + + + {emlContent.attachments.map((attachment, index) => ( + + + + {attachment.name ?? 'No name'} + + + downloadAttachment(attachment)} + > + + Download + + {(attachment?.contentType === undefined || + attachment?.contentType?.includes('text') || + attachment?.contentType?.includes('pdf') || + attachment?.contentType?.includes('image') || + attachment?.contentType?.includes('rfc822')) && ( + downloadAttachment(attachment, true)} + > + + View + + )} + + + ))} + + + )} + + {(emlContent?.text || emlContent?.html) && ( + + + {messageHtml ? ( +
    {messageHtml}
    + ) : ( +
    + +
    + )} +
    +
    + )} +
    + + )} + + ) +} + +MessageViewer.propTypes = { + emlFile: PropTypes.string, +} + +const MessageViewerPage = () => { + const [emlFile, setEmlFile] = useState(null) + const onDrop = useCallback((acceptedFiles) => { + acceptedFiles.forEach((file) => { + const reader = new FileReader() + + reader.onabort = () => console.log('file reading was aborted') + reader.onerror = () => console.log('file reading has failed') + reader.onload = () => { + setEmlFile(reader.result) + } + reader.readAsText(file) + }) + }, []) + + return ( + + + {emlFile && } + + ) +} + +export default MessageViewerPage From 9b51796b206dde86252543a00c4bebaf690d81b1 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Wed, 31 Jul 2024 23:33:12 +0200 Subject: [PATCH 440/475] upgrade user schedulder experience. --- src/views/identity/administration/AddUser.jsx | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/views/identity/administration/AddUser.jsx b/src/views/identity/administration/AddUser.jsx index 96186c358ed9..ad772b208d1a 100644 --- a/src/views/identity/administration/AddUser.jsx +++ b/src/views/identity/administration/AddUser.jsx @@ -37,8 +37,12 @@ import useQuery from 'src/hooks/useQuery' import Select from 'react-select' import { useNavigate } from 'react-router-dom' import { OnChange } from 'react-final-form-listeners' +import DatePicker from 'react-datepicker' +import 'react-datepicker/dist/react-datepicker.css' const AddUser = () => { + const currentDate = new Date() + const [startDate, setStartDate] = useState(currentDate) let navigate = useNavigate() const [addedAttributes, setAddedAttribute] = React.useState(0) const tenant = useSelector((state) => state.app.currentTenant) @@ -81,6 +85,8 @@ const AddUser = () => { values.addedAttributes.push({ Key: key, Value: values.defaultAttributes[key].Value }) }) } + const unixTime = Math.floor(startDate.getTime() / 1000) + const shippedValues = { AddedAliases: values.addedAliases ? values.addedAliases : '', BusinessPhone: values.businessPhones, @@ -106,6 +112,10 @@ const AddUser = () => { tenantID: tenantDomain, addedAttributes: values.addedAttributes, setManager: values.setManager, + Scheduled: values.Scheduled?.enabled ? { enabled: true, date: unixTime } : { enabled: false }, + PostExecution: values.Scheduled?.enabled + ? { webhook: values.webhook, psa: values.psa, email: values.email } + : '', ...values.license, } //window.alert(JSON.stringify(shippedValues)) @@ -408,6 +418,33 @@ const AddUser = () => { /> {usersError && Failed to load list of users}
    + + + +
    + + + + + setStartDate(date)} + /> + + + + + + + + + + From 3f20d802064b8322a32aa9fad0dfe129c321714a Mon Sep 17 00:00:00 2001 From: John Duprey Date: Wed, 31 Jul 2024 19:42:03 -0400 Subject: [PATCH 441/475] message view bugfixes --- .../email-exchange/tools/MessageViewer.jsx | 66 ++++++++++++------- 1 file changed, 41 insertions(+), 25 deletions(-) diff --git a/src/views/email-exchange/tools/MessageViewer.jsx b/src/views/email-exchange/tools/MessageViewer.jsx index 314762758830..52d7c3b637a4 100644 --- a/src/views/email-exchange/tools/MessageViewer.jsx +++ b/src/views/email-exchange/tools/MessageViewer.jsx @@ -23,9 +23,8 @@ import DOMPurify from 'dompurify' import ReactHtmlParser from 'react-html-parser' import CippDropzone from 'src/components/utilities/CippDropzone' -const MessageViewer = ({ emlFile }) => { +const MessageViewer = ({ emailSource }) => { const [emlContent, setEmlContent] = useState(null) - const [emailSource, setEmailSource] = useState(emlFile) const [emlError, setEmlError] = useState(false) const [messageHtml, setMessageHtml] = useState('') @@ -63,39 +62,57 @@ const MessageViewer = ({ emlFile }) => { } const downloadAttachment = (attachment, newTab = false) => { - if (attachment?.data) { - var contentType = attachment?.contentType?.split(';')[0] ?? 'text/plain' - var fileBytes = attachment.data - var fileName = attachment.name - downloadFileBytes(fileName, fileBytes, contentType, newTab) - } else { - downloadFile(attachment.name, attachment.data64) + var contentType = attachment?.contentType?.split(';')[0] ?? 'text/plain' + var fileBytes = attachment.data + if (fileBytes instanceof Uint8Array && attachment?.data64) { + fileBytes = new Uint8Array( + atob(attachment.data64) + .split('') + .map((c) => c.charCodeAt(0)), + ) } - } - - const downloadFile = (fileName, base64Content, newTab) => { - const link = document.createElement('a') - link.href = `data:application/octet-stream;base64,${base64Content}` - link.download = fileName - link.click() - } - - const downloadFileBytes = (fileName, fileBytes, contentType, newTab = false) => { + var fileName = attachment.name const blob = new Blob([fileBytes], { type: contentType ?? 'application/octet-stream' }) const url = URL.createObjectURL(blob) const link = document.createElement('a') if (newTab) { if (contentType.includes('rfc822')) { var content = fileBytes - const nestedMessage = + const nestedMessage = ModalService.open({ body: nestedMessage, title: fileName, size: 'lg', }) + } else if (contentType.includes('pdf')) { + const embeddedPdf = + ModalService.open({ + body: embeddedPdf, + title: fileName, + size: 'lg', + }) + } else if (contentType.includes('image')) { + const embeddedImage = {fileName} + ModalService.open({ + body: embeddedImage, + title: fileName, + size: 'lg', + }) + } else if (contentType.includes('text')) { + const textContent = fileBytes + ModalService.open({ + data: textContent, + componentType: 'codeblock', + title: fileName, + size: 'lg', + }) + setTimeout(() => { + URL.revokeObjectURL(url) + }, 1000) } else { const newWindow = window.open() newWindow.location.href = url + URL.revokeObjectURL(url) } } else { link.href = url @@ -145,7 +162,7 @@ const MessageViewer = ({ emlFile }) => { } } }) - }, [emailSource, setMessageHtml, setEmailSource, setEmlError, setEmlContent]) + }, [emailSource, setMessageHtml, setEmlError, setEmlContent]) var buttons = EmailButtons(emailSource) @@ -238,7 +255,7 @@ const MessageViewer = ({ emlFile }) => { className="dropdown-item" onClick={() => downloadAttachment(attachment, true)} > - + View )} @@ -274,7 +291,7 @@ const MessageViewer = ({ emlFile }) => { } MessageViewer.propTypes = { - emlFile: PropTypes.string, + emailSource: PropTypes.string, } const MessageViewerPage = () => { @@ -282,7 +299,6 @@ const MessageViewerPage = () => { const onDrop = useCallback((acceptedFiles) => { acceptedFiles.forEach((file) => { const reader = new FileReader() - reader.onabort = () => console.log('file reading was aborted') reader.onerror = () => console.log('file reading has failed') reader.onload = () => { @@ -302,7 +318,7 @@ const MessageViewerPage = () => { dropMessage="Drag an EML file or click to add" maxFiles={1} /> - {emlFile && } + {emlFile && } ) } From cff54a8fbd26c98f154a9cade3af684cba243d10 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Wed, 31 Jul 2024 20:00:02 -0400 Subject: [PATCH 442/475] add blob: to content-security-policy --- staticwebapp.config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/staticwebapp.config.json b/staticwebapp.config.json index 8a0fafca07d4..74988468595b 100644 --- a/staticwebapp.config.json +++ b/staticwebapp.config.json @@ -103,7 +103,7 @@ } }, "globalHeaders": { - "content-security-policy": "default-src https: 'unsafe-eval' 'unsafe-inline'; object-src 'none'; img-src 'self' data: *" + "content-security-policy": "default-src https: 'unsafe-eval' 'unsafe-inline'; object-src 'self' blob:; img-src 'self' blob: data: *" }, "mimeTypes": { ".json": "text/json" From ff56eef918f8e04122c6983bc67c51680185845a Mon Sep 17 00:00:00 2001 From: John Duprey Date: Wed, 31 Jul 2024 20:05:33 -0400 Subject: [PATCH 443/475] add blob to default-src --- staticwebapp.config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/staticwebapp.config.json b/staticwebapp.config.json index 74988468595b..0c36ddcac257 100644 --- a/staticwebapp.config.json +++ b/staticwebapp.config.json @@ -103,7 +103,7 @@ } }, "globalHeaders": { - "content-security-policy": "default-src https: 'unsafe-eval' 'unsafe-inline'; object-src 'self' blob:; img-src 'self' blob: data: *" + "content-security-policy": "default-src https: blob: 'unsafe-eval' 'unsafe-inline'; object-src 'self' blob:; img-src 'self' blob: data: *" }, "mimeTypes": { ".json": "text/json" From f978031b21ed9b612e57cbeb32b47ea122e28b44 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Wed, 31 Jul 2024 20:19:16 -0400 Subject: [PATCH 444/475] sanitize secure score html --- .../tenant/administration/SecureScore.jsx | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/views/tenant/administration/SecureScore.jsx b/src/views/tenant/administration/SecureScore.jsx index be036997339c..bfa9d2e751c2 100644 --- a/src/views/tenant/administration/SecureScore.jsx +++ b/src/views/tenant/administration/SecureScore.jsx @@ -28,6 +28,8 @@ import { CellTip, cellGenericFormatter } from 'src/components/tables/CellGeneric import { CippCallout } from 'src/components/layout' import CippPrettyCard from 'src/components/contentcards/CippPrettyCard' import { TableModalButton } from 'src/components/buttons' +import DOMPurify from 'dompurify' +import ReactHtmlParser from 'react-html-parser' const SecureScore = () => { const textRef = useRef() @@ -66,6 +68,12 @@ const SecureScore = () => { }, }) + const sanitizeHtml = (html) => { + var sanitizedHtml = DOMPurify.sanitize(html) + var parsedHtml = ReactHtmlParser(sanitizedHtml) + return parsedHtml + } + useEffect(() => { if (isSuccess) { setTranslatedData(securescore.Results[0]) @@ -341,23 +349,16 @@ const SecureScore = () => {
    Description
    -
    +
    + {sanitizeHtml(`${info.description} ${info.implementationStatus}`)} +
    {info.scoreInPercentage !== 100 && (
    Remediation Recommendation
    - { -
    - } + {
    {sanitizeHtml(info.remediation)}
    }
    )} From e4e5ab15c86afa76f2e77028728e1bb599012c6b Mon Sep 17 00:00:00 2001 From: John Duprey Date: Wed, 31 Jul 2024 20:44:54 -0400 Subject: [PATCH 445/475] Fix revokesession bulk action --- src/views/identity/administration/Users.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/identity/administration/Users.jsx b/src/views/identity/administration/Users.jsx index 3bd663d7e063..669e401a3611 100644 --- a/src/views/identity/administration/Users.jsx +++ b/src/views/identity/administration/Users.jsx @@ -589,7 +589,7 @@ const Users = (row) => { label: 'Revoke sessions', color: 'info', modal: true, - modalUrl: `/api/ExecRevokeSessions?Enable=true&TenantFilter=!Tenant&ID=!userPrincipalName`, + modalUrl: `/api/ExecRevokeSessions?Enable=true&TenantFilter=!Tenant&ID=!id&Username=!userPrincipalName`, modalMessage: 'Are you sure you want to revoke all sessions for these users?', }, { From 8be3b6d85b95ef903ad1522d667483b4c845cbed Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 1 Aug 2024 01:11:50 -0400 Subject: [PATCH 446/475] SAM Roles --- .../cipp/app-settings/SettingsSuperAdmin.jsx | 38 +---- .../components/SettingsSAMRoles.jsx | 133 ++++++++++++++++++ 2 files changed, 135 insertions(+), 36 deletions(-) create mode 100644 src/views/cipp/app-settings/components/SettingsSAMRoles.jsx diff --git a/src/views/cipp/app-settings/SettingsSuperAdmin.jsx b/src/views/cipp/app-settings/SettingsSuperAdmin.jsx index 4e38038fb68c..18cb6b397980 100644 --- a/src/views/cipp/app-settings/SettingsSuperAdmin.jsx +++ b/src/views/cipp/app-settings/SettingsSuperAdmin.jsx @@ -7,6 +7,7 @@ import { CippCallout } from 'src/components/layout/index.js' import CippAccordionItem from 'src/components/contentcards/CippAccordionItem' import SettingsCustomRoles from 'src/views/cipp/app-settings/components/SettingsCustomRoles' import CippButtonCard from 'src/components/contentcards/CippButtonCard' +import SettingsSAMRoles from './components/SettingsSAMRoles' export function SettingsSuperAdmin() { const partnerConfig = useGenericGetRequestQuery({ @@ -65,46 +66,11 @@ export function SettingsSuperAdmin() {

    - - -

    Tenant Mode

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

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

    +

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

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

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

    - { - return ( - - - -
    - ({ - name: role.Name, - value: role.ObjectId, - }))} - isLoading={roleListFetching} - multi={true} - refreshFunction={() => refetchRoleList()} - placeholder="Select admin roles" - /> -
    -
    -
    Selected Tenants
    - handleTenantChange(e)} - /> -
    -
    -
    - - {postResults.isSuccess && ( - {postResults.data.Results} - )} + {roleListSuccess && ( + { + return ( + - - - +
    + ({ + name: role.Name, + value: role.ObjectId, + }))} + multi={true} + refreshFunction={() => refetchRoleList()} + placeholder="Select admin roles" /> - Save - +
    +
    +
    Selected Tenants
    + handleTenantChange(e)} + /> +
    -
    -
    - ) - }} - /> + + {postResults.isSuccess && ( + {postResults.data.Results} + )} + + + + + Save + + + + + + ) + }} + /> + )}
    ) From 47e8e7f59feb14bdc2439a85b948df943317fbc1 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Thu, 1 Aug 2024 15:29:53 +0200 Subject: [PATCH 451/475] Add edit named locations --- .../tenant/conditional/NamedLocations.jsx | 87 +++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/src/views/tenant/conditional/NamedLocations.jsx b/src/views/tenant/conditional/NamedLocations.jsx index b7f1ebb0f68c..817528a2078c 100644 --- a/src/views/tenant/conditional/NamedLocations.jsx +++ b/src/views/tenant/conditional/NamedLocations.jsx @@ -22,6 +22,88 @@ function DateNotNull(date) { return date.toString().trim() + 'Z' } +const Offcanvas = (row, rowIndex, formatExtraData) => { + const tenant = useSelector((state) => state.app.currentTenant) + const [ocVisible, setOCVisible] = useState(false) + return ( + <> + setOCVisible(true)}> + + + setOCVisible(false)} + /> + + ) +} const columns = [ { name: 'Name', @@ -62,6 +144,11 @@ const columns = [ exportSelector: 'modifiedDateTime', maxWidth: '150px', }, + { + name: 'Actions', + cell: Offcanvas, + maxWidth: '80px', + }, ] const NamedLocationsList = () => { From c70d679fe0570187774a9a98fb5a912feefd5ebc Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Thu, 1 Aug 2024 15:43:52 +0200 Subject: [PATCH 452/475] test --- src/views/cipp/ExtensionMappings.jsx | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/views/cipp/ExtensionMappings.jsx b/src/views/cipp/ExtensionMappings.jsx index 4de976555ccc..816a679e9905 100644 --- a/src/views/cipp/ExtensionMappings.jsx +++ b/src/views/cipp/ExtensionMappings.jsx @@ -218,11 +218,7 @@ export default function ExtensionMappings({ type, fieldMappings = false, autoMap { - return !Object.keys(listMappingBackendResult.data?.Mappings).includes( - tenant.customerId, - ) - }).map((tenant) => ({ + values={listMappingBackendResult.data?.Tenants.map((tenant) => ({ name: tenant.displayName, value: tenant.customerId, }))} From 930f99700d2b3d1c3c20c800d202d3ece43f458c Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Thu, 1 Aug 2024 15:48:47 +0200 Subject: [PATCH 453/475] test fix for extension multi mappings --- src/views/cipp/ExtensionMappings.jsx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/views/cipp/ExtensionMappings.jsx b/src/views/cipp/ExtensionMappings.jsx index 816a679e9905..5bcf9e23e8e8 100644 --- a/src/views/cipp/ExtensionMappings.jsx +++ b/src/views/cipp/ExtensionMappings.jsx @@ -218,7 +218,11 @@ export default function ExtensionMappings({ type, fieldMappings = false, autoMap ({ + values={listMappingBackendResult.data?.Tenants.filter((tenant) => { + return !Object.keys(listMappingBackendResult.data?.Mappings).includes( + tenant.customerId, + ) + }).map((tenant) => ({ name: tenant.displayName, value: tenant.customerId, }))} @@ -247,9 +251,7 @@ export default function ExtensionMappings({ type, fieldMappings = false, autoMap if ( mappingValue.value !== undefined && mappingValue.value !== '-1' && - Object.values(mappingArray) - .map((item) => item.companyId) - .includes(mappingValue.value) === false + Object.values(mappingArray).map((item) => item.companyId) ) { setMappingArray([ ...mappingArray, From 1d94b1e20fa67bc9e2f54de62462cb2c61c65360 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Thu, 1 Aug 2024 15:57:45 +0200 Subject: [PATCH 454/475] fallback to actual id --- src/components/tables/CellLicense.jsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/tables/CellLicense.jsx b/src/components/tables/CellLicense.jsx index 50b88b978c4f..ff5df5329743 100644 --- a/src/components/tables/CellLicense.jsx +++ b/src/components/tables/CellLicense.jsx @@ -8,6 +8,8 @@ export function CellLicense({ cell }) { if (licenseAssignment.skuId == M365Licenses[x].GUID) { licenses.push(M365Licenses[x].Product_Display_Name) break + } else { + licenses.push(licenseAssignment.skuId) } } }) From ed1e05be4004d18af54533032feb522e000532b0 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 1 Aug 2024 14:14:25 -0400 Subject: [PATCH 455/475] Audit logs --- src/_nav.jsx | 9 +- src/importsMap.jsx | 3 +- src/routes.json | 10 +- .../tenant/administration/ListAlertsQueue.jsx | 2 +- .../tenant/administration/ListAuditLogs.jsx | 233 ++++++++++++++++++ 5 files changed, 251 insertions(+), 6 deletions(-) create mode 100644 src/views/tenant/administration/ListAuditLogs.jsx diff --git a/src/_nav.jsx b/src/_nav.jsx index 59d2980e2ae1..bc1505aa763e 100644 --- a/src/_nav.jsx +++ b/src/_nav.jsx @@ -145,8 +145,13 @@ const _nav = [ }, { component: CNavItem, - name: 'Alerts', - to: '/tenant/administration/alertsqueue', + name: 'Alert Configuration', + to: '/tenant/administration/alert-configuration', + }, + { + component: CNavItem, + name: 'Audit Logs', + to: '/tenant/administration/audit-logs', }, { component: CNavItem, diff --git a/src/importsMap.jsx b/src/importsMap.jsx index f7c2a6e83234..3708a44f4ed9 100644 --- a/src/importsMap.jsx +++ b/src/importsMap.jsx @@ -42,7 +42,8 @@ import React from 'react' "/tenant/administration/domains": React.lazy(() => import('./views/tenant/administration/Domains')), "/tenant/administration/alertswizard": React.lazy(() => import('./views/tenant/administration/AlertWizard')), "/tenant/administration/alertrules": React.lazy(() => import('./views/tenant/administration/AlertRules')), - "/tenant/administration/alertsqueue": React.lazy(() => import('./views/tenant/administration/ListAlertsQueue')), + "/tenant/administration/alert-configuration": React.lazy(() => import('./views/tenant/administration/ListAlertsQueue')), + "/tenant/administration/audit-logs": React.lazy(() => import('./views/tenant/administration/ListAuditLogs')), "/tenant/administration/graph-explorer": React.lazy(() => import('./views/tenant/administration/GraphExplorer')), "/tenant/administration/service-health": React.lazy(() => import('./views/tenant/administration/ServiceHealth')), "/tenant/administration/enterprise-apps": React.lazy(() => import('./views/tenant/administration/ListEnterpriseApps')), diff --git a/src/routes.json b/src/routes.json index be956782af6d..73f0383bbd20 100644 --- a/src/routes.json +++ b/src/routes.json @@ -279,11 +279,17 @@ "allowedRoles": ["admin", "editor", "readonly"] }, { - "path": "/tenant/administration/alertsqueue", - "name": "Alerts Queue", + "path": "/tenant/administration/alert-configuration", + "name": "Alert Configuration", "component": "views/tenant/administration/ListAlertsQueue", "allowedRoles": ["admin", "editor", "readonly"] }, + { + "path": "/tenant/administration/audit-logs", + "name": "Audit Logs", + "component": "views/tenant/administration/ListAuditLogs", + "allowedRoles": ["admin", "editor", "readonly"] + }, { "path": "/tenant/administration/graph-explorer", "name": "Graph Explorer", diff --git a/src/views/tenant/administration/ListAlertsQueue.jsx b/src/views/tenant/administration/ListAlertsQueue.jsx index 7efe849c13a4..7a715188a824 100644 --- a/src/views/tenant/administration/ListAlertsQueue.jsx +++ b/src/views/tenant/administration/ListAlertsQueue.jsx @@ -65,7 +65,7 @@ const ListClassicAlerts = () => { allTenants: true, helpContext: 'https://google.com', }} - title="Alerts List" + title="Alert Configuration" titleButton={ { + const [interval, setInterval] = React.useState('d') + const [time, setTime] = React.useState(1) + const [relativeTime, setRelativeTime] = React.useState('1d') + const [startDate, setStartDate] = React.useState(null) + const [endDate, setEndDate] = React.useState(null) + const [visibleA, setVisibleA] = React.useState(true) + const [tenantColumnSet, setTenantColumn] = React.useState(false) + const tenant = useSelector((state) => state.app.currentTenant) + + useEffect(() => { + if (tenant.defaultDomainName === 'AllTenants') { + setTenantColumn(false) + } + if (tenant.defaultDomainName !== 'AllTenants') { + setTenantColumn(true) + } + }, [tenant.defaultDomainName, tenantColumnSet]) + + const handleSearch = (values) => { + if (values.dateFilter === 'relative') { + setRelativeTime(`${values.Time}${values.Interval}`) + setStartDate(null) + setEndDate(null) + } else if (values.dateFilter === 'startEnd') { + setRelativeTime(null) + setStartDate(values.startDate) + setEndDate(values.endDate) + } + setVisibleA(false) + } + + const Actions = () => { + return ( + + + + + + ) + } + + const columns = [ + { + name: 'Timestamp', + selector: (row) => row['Timestamp'], + sortable: true, + exportSelector: 'Timestamp', + cell: cellDateFormatter({ format: 'short' }), + maxWidth: '200px', + }, + { + name: 'Tenant', + selector: (row) => row['Tenant'], + exportSelector: 'Tenant', + omit: !tenantColumnSet, + cell: cellGenericFormatter(), + maxWidth: '150px', + }, + { + name: 'Title', + selector: (row) => row['Title'], + exportSelector: 'Title', + cell: cellGenericFormatter(), + }, + { + name: 'Actions', + cell: Actions, + maxWidth: '100px', + }, + ] + return ( +
    + + + + + + Search Options + setVisibleA(!visibleA)} + > + + + + + + + + + + { + return ( + + + + Date Filter Type +
    + +
    +
    +
    +
    + + + + Relative Time + + + Last + + + {({ input, meta }) => } + + + {({ input, meta }) => ( + + + + + + )} + + + + + + + + + + + + + + + + + + + + Search + + + +
    + ) + }} + /> +
    +
    +
    +
    +
    +
    + +
    + ) +} + +export default ListAuditLogs From 70853600b9183487bffe959e8168ae93b8e6d8eb Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 1 Aug 2024 19:40:38 -0400 Subject: [PATCH 456/475] Add refresh config following a save --- src/views/cipp/Extensions.jsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/views/cipp/Extensions.jsx b/src/views/cipp/Extensions.jsx index eec80f5a1068..5923599a6f66 100644 --- a/src/views/cipp/Extensions.jsx +++ b/src/views/cipp/Extensions.jsx @@ -39,6 +39,8 @@ export default function CIPPExtensions() { setExtensionconfig({ path: 'api/ExecExtensionsConfig', values: values, + }).then((res) => { + listBackend({ path: 'api/ListExtensionsConfig' }) }) } From aae375a43a93b2ca62889330c6c7f9583407f824 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Fri, 2 Aug 2024 12:23:34 +0200 Subject: [PATCH 457/475] updates to layout --- .../tenant/standards/ListAppliedStandards.jsx | 36 ++++--------------- 1 file changed, 6 insertions(+), 30 deletions(-) diff --git a/src/views/tenant/standards/ListAppliedStandards.jsx b/src/views/tenant/standards/ListAppliedStandards.jsx index f60370975ee6..d93d6609148c 100644 --- a/src/views/tenant/standards/ListAppliedStandards.jsx +++ b/src/views/tenant/standards/ListAppliedStandards.jsx @@ -751,23 +751,15 @@ const ApplyNewStandard = () => { }, ].map((template, index) => ( - +
    {template.name}
    Deploy {template.name}
    - -
    Report
    - -
    - -
    Alert
    - -
    Remediate
    - +
    Settings
    {template.templates.isSuccess && ( {
    ))} - +
    Autopilot Profile
    Deploy Autopilot profile
    - -
    Report
    - -
    - -
    Alert
    - -
    Remediate
    - +
    Settings
    @@ -938,23 +922,15 @@ const ApplyNewStandard = () => { - +
    Autopilot Status Page
    Deploy Autopilot Status Page
    - -
    Report
    - -
    - -
    Alert
    - -
    Remediate
    - +
    Settings
    From fb51fb943a128b52c05ecc8fa68cef2db29e6ca9 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Fri, 2 Aug 2024 13:31:04 +0200 Subject: [PATCH 458/475] new standards apply templates for intune --- .../tenant/standards/ListAppliedStandards.jsx | 123 ++++++++++++++++-- 1 file changed, 112 insertions(+), 11 deletions(-) diff --git a/src/views/tenant/standards/ListAppliedStandards.jsx b/src/views/tenant/standards/ListAppliedStandards.jsx index d93d6609148c..d591ff4b7168 100644 --- a/src/views/tenant/standards/ListAppliedStandards.jsx +++ b/src/views/tenant/standards/ListAppliedStandards.jsx @@ -29,7 +29,12 @@ import { useLazyGenericGetRequestQuery, useLazyGenericPostRequestQuery, } from 'src/store/api/app' -import { faCheck, faCircleNotch, faExclamationTriangle } from '@fortawesome/free-solid-svg-icons' +import { + faCheck, + faCircleNotch, + faExclamationTriangle, + faTrash, +} from '@fortawesome/free-solid-svg-icons' import { CippCallout, CippContentCard, CippPage } from 'src/components/layout' import { useSelector } from 'react-redux' import { ModalService, validateAlphabeticalSort } from 'src/components/utilities' @@ -368,6 +373,32 @@ const ApplyNewStandard = () => { setEnabledWarningsCount, ]) + const handleAddIntuneTemplate = (form) => { + const formvalues = form.getState().values + const newTemplate = { + label: formvalues.intunedataList.label, + value: formvalues.intunedataList.value, + AssignedTo: + formvalues.IntuneAssignto === 'customGroup' + ? formvalues.customGroup + : formvalues.IntuneAssignto, + } + const originalTemplates = formvalues.standards?.IntuneTemplate?.TemplateList || [] + const updatedTemplateList = [...originalTemplates, newTemplate] + + form.change('standards.IntuneTemplate.TemplateList', updatedTemplateList) + form.change('intunedataList', undefined) + form.change('intuneAssignTo', undefined) + form.change('customGroup', undefined) + } + const handleRemoveDeployedTemplate = (form, row) => { + console.log(row) + const formvalues = form.getState().values + const updatedTemplateList = formvalues.standards.IntuneTemplate.TemplateList.filter( + (template) => template.value !== row.value, + ) + form.change('standards.IntuneTemplate.TemplateList', updatedTemplateList) + } return ( <> @@ -484,7 +515,7 @@ const ApplyNewStandard = () => { }, }} onSubmit={handleSubmit} - render={({ handleSubmit, submitting, values }) => { + render={({ handleSubmit, submitting, values, form }) => { return ( @@ -728,6 +759,7 @@ const ApplyNewStandard = () => { switchName: 'standards.IntuneTemplate', assignable: true, templates: intuneTemplates, + table: true, }, { name: 'Transport Rule Template', @@ -761,11 +793,53 @@ const ApplyNewStandard = () => {
    Settings
    + {template.table && ( + row['label'], + sortable: true, + exportSelector: 'name', + cell: cellGenericFormatter(), + }, + { + name: 'Assigned to', + selector: (row) => row['AssignedTo'], + sortable: true, + exportSelector: 'GUID', + }, + { + name: 'Actions', + cell: (row) => ( + + handleRemoveDeployedTemplate(form, row) + } + > + + + ), + }, + ]} + /> + )} {template.templates.isSuccess && ( { <> + handleAddIntuneTemplate(form)}> + Add to deployment + )}
    From 5facbd7cd4fd8a2c6a1ced151439d7cfe38f3dd9 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Fri, 2 Aug 2024 13:50:31 +0200 Subject: [PATCH 459/475] add remove of old undefined. --- src/views/tenant/standards/ListAppliedStandards.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/tenant/standards/ListAppliedStandards.jsx b/src/views/tenant/standards/ListAppliedStandards.jsx index d591ff4b7168..10eab27c96fd 100644 --- a/src/views/tenant/standards/ListAppliedStandards.jsx +++ b/src/views/tenant/standards/ListAppliedStandards.jsx @@ -385,7 +385,7 @@ const ApplyNewStandard = () => { } const originalTemplates = formvalues.standards?.IntuneTemplate?.TemplateList || [] const updatedTemplateList = [...originalTemplates, newTemplate] - + form.change('standards.IntuneTemplate.AssignTo', undefined) form.change('standards.IntuneTemplate.TemplateList', updatedTemplateList) form.change('intunedataList', undefined) form.change('intuneAssignTo', undefined) From c7b6f31f510dedb7e9b633e4986712463be08a33 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Fri, 2 Aug 2024 15:47:40 +0200 Subject: [PATCH 460/475] revert due to license column bug. --- src/components/tables/CellLicense.jsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/components/tables/CellLicense.jsx b/src/components/tables/CellLicense.jsx index ff5df5329743..50b88b978c4f 100644 --- a/src/components/tables/CellLicense.jsx +++ b/src/components/tables/CellLicense.jsx @@ -8,8 +8,6 @@ export function CellLicense({ cell }) { if (licenseAssignment.skuId == M365Licenses[x].GUID) { licenses.push(M365Licenses[x].Product_Display_Name) break - } else { - licenses.push(licenseAssignment.skuId) } } }) From fde2501818265f9e52bbc2209be01b52bc7d3423 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Fri, 2 Aug 2024 11:36:59 -0400 Subject: [PATCH 461/475] Add message header parsing --- .../email-exchange/tools/MessageViewer.jsx | 34 ++++++++++++++----- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/src/views/email-exchange/tools/MessageViewer.jsx b/src/views/email-exchange/tools/MessageViewer.jsx index 52d7c3b637a4..f010c0b37c4f 100644 --- a/src/views/email-exchange/tools/MessageViewer.jsx +++ b/src/views/email-exchange/tools/MessageViewer.jsx @@ -27,6 +27,7 @@ const MessageViewer = ({ emailSource }) => { const [emlContent, setEmlContent] = useState(null) const [emlError, setEmlError] = useState(false) const [messageHtml, setMessageHtml] = useState('') + const [emlHeaders, setEmlHeaders] = useState(null) const getAttachmentIcon = (contentType) => { if (contentType.includes('image')) { @@ -126,21 +127,32 @@ const MessageViewer = ({ emailSource }) => { return d instanceof Date && !isNaN(d) } - const showEmailModal = (emailSource) => { + const showEmailModal = (emailSource, title = 'Email Source') => { ModalService.open({ data: emailSource, componentType: 'codeblock', - title: 'Email Source', + title: title, size: 'lg', }) } - const EmailButtons = (emailSource) => { + const EmailButtons = (emailHeaders, emailSource) => { + const emailSourceBytes = new TextEncoder().encode(emailSource) + const blob = new Blob([emailSourceBytes], { type: 'message/rfc822' }) + const url = URL.createObjectURL(blob) return ( - showEmailModal(emailSource)}> - - View Source - + + {emailHeaders && ( + showEmailModal(emailHeaders, 'Email Headers')} className="me-2"> + + View Headers + + )} + showEmailModal(emailSource)}> + + View Source + + ) } @@ -150,6 +162,7 @@ const MessageViewer = ({ emailSource }) => { setEmlError(true) setEmlContent(null) setMessageHtml(null) + setEmlHeaders(null) } else { setEmlContent(ReadEmlJson) setEmlError(false) @@ -160,11 +173,14 @@ const MessageViewer = ({ emailSource }) => { } else { setMessageHtml(null) } + const header_regex = /(?:^[\w-]+:\s?.*(?:\r?\n[ \t].*)*\r?\n?)+/gm + const headers = emailSource.match(header_regex) + setEmlHeaders(headers ? headers[0] : null) } }) - }, [emailSource, setMessageHtml, setEmlError, setEmlContent]) + }, [emailSource, setMessageHtml, setEmlError, setEmlContent, setEmlHeaders]) - var buttons = EmailButtons(emailSource) + var buttons = EmailButtons(emlHeaders, emailSource) return ( <> From c755dce05a4775570b719b934a757166ba42d52f Mon Sep 17 00:00:00 2001 From: John Duprey Date: Fri, 2 Aug 2024 12:31:27 -0400 Subject: [PATCH 462/475] Audit Logs Add basic offcanvas --- .../tenant/administration/ListAuditLogs.jsx | 63 +++++++++++++++++-- 1 file changed, 57 insertions(+), 6 deletions(-) diff --git a/src/views/tenant/administration/ListAuditLogs.jsx b/src/views/tenant/administration/ListAuditLogs.jsx index 2ec02c53cf6c..4e1d30392b3d 100644 --- a/src/views/tenant/administration/ListAuditLogs.jsx +++ b/src/views/tenant/administration/ListAuditLogs.jsx @@ -26,8 +26,13 @@ import { import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { Condition, RFFCFormInput, RFFCFormRadioList } from 'src/components/forms' import { Field, Form } from 'react-final-form' +import { useSearchParams } from 'react-router-dom' +import { CippCodeBlock, CippOffcanvas } from 'src/components/utilities' const ListAuditLogs = () => { + // get query parameters + const [searchParams, setSearchParams] = useSearchParams() + const logId = searchParams.get('LogId') const [interval, setInterval] = React.useState('d') const [time, setTime] = React.useState(1) const [relativeTime, setRelativeTime] = React.useState('1d') @@ -59,13 +64,58 @@ const ListAuditLogs = () => { setVisibleA(false) } - const Actions = () => { + const Actions = (row) => { + const [visible, setVisible] = React.useState(false) return ( - - - - - + <> + setVisible(true)}> + + + + + setVisible(false)} + visible={visible} + addedClass="offcanvas-large" + placement="end" + > + + + +

    Log Details

    +
    +
    + + {row?.Data?.ActionText && ( + + + + + {row?.Data?.ActionText} + + + + )} + + +

    Raw Log

    + +
    +
    +
    +
    +
    + ) } @@ -219,6 +269,7 @@ const ListAuditLogs = () => { RelativeTime: relativeTime, StartDate: startDate, EndDate: endDate, + LogId: logId, }, tableProps: { selectableRows: true, From 53445e671a41ba4ffd0c7b36e8c095b2e1f89493 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Fri, 2 Aug 2024 18:39:50 +0200 Subject: [PATCH 463/475] release ready --- src/importsMap.jsx | 1 + src/routes.json | 6 + src/views/cipp/TemplateLibrary.jsx | 149 ++++++++++++++++++ .../intune/MEMListPolicyTemplates.jsx | 6 + .../tenant/conditional/ListCATemplates.jsx | 6 + 5 files changed, 168 insertions(+) create mode 100644 src/views/cipp/TemplateLibrary.jsx diff --git a/src/importsMap.jsx b/src/importsMap.jsx index 3708a44f4ed9..49f07b806f00 100644 --- a/src/importsMap.jsx +++ b/src/importsMap.jsx @@ -2,6 +2,7 @@ import React from 'react' export const importsMap = { "/home": React.lazy(() => import('./views/home/Home')), "/cipp/logs": React.lazy(() => import('./views/cipp/Logs')), + "/cipp/template-library": React.lazy(() => import('./views/cipp/TemplateLibrary')), "/cipp/scheduler": React.lazy(() => import('./views/cipp/Scheduler')), "/cipp/statistics": React.lazy(() => import('./views/cipp/Statistics')), "/cipp/404": React.lazy(() => import('./views/pages/page404/Page404')), diff --git a/src/routes.json b/src/routes.json index 73f0383bbd20..532343cc83e4 100644 --- a/src/routes.json +++ b/src/routes.json @@ -11,6 +11,12 @@ "component": "views/cipp/Logs", "allowedRoles": ["admin", "editor", "readonly"] }, + { + "path": "/cipp/template-library", + "name": "Logs", + "component": "views/cipp/TemplateLibrary", + "allowedRoles": ["admin", "editor", "readonly"] + }, { "path": "/cipp/scheduler", "name": "Scheduler", diff --git a/src/views/cipp/TemplateLibrary.jsx b/src/views/cipp/TemplateLibrary.jsx new file mode 100644 index 000000000000..4dbf493d2293 --- /dev/null +++ b/src/views/cipp/TemplateLibrary.jsx @@ -0,0 +1,149 @@ +import React, { useState } from 'react' +import { CAlert, CButton, CCallout, CCol, CForm, CRow, CSpinner, CTooltip } from '@coreui/react' +import { useSelector } from 'react-redux' +import { Field, Form } from 'react-final-form' +import { RFFCFormInput, RFFCFormSwitch } from 'src/components/forms' +import { + useGenericGetRequestQuery, + useLazyGenericGetRequestQuery, + useLazyGenericPostRequestQuery, +} from 'src/store/api/app' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { faCircleNotch, faEdit, faEye } from '@fortawesome/free-solid-svg-icons' +import { CippPage, CippPageList } from 'src/components/layout' +import 'react-datepicker/dist/react-datepicker.css' +import { ModalService, TenantSelector } from 'src/components/utilities' +import arrayMutators from 'final-form-arrays' +import { useListConditionalAccessPoliciesQuery } from 'src/store/api/tenants' +import CippButtonCard from 'src/components/contentcards/CippButtonCard' +import { CellTip, cellGenericFormatter } from 'src/components/tables/CellGenericFormat' +import { cellBadgeFormatter, cellDateFormatter } from 'src/components/tables' +import { Alert } from '@coreui/coreui' + +const TemplateLibrary = () => { + const [ExecuteGetRequest, getResults] = useLazyGenericGetRequestQuery() + const tenantDomain = useSelector((state) => state.app.currentTenant.defaultDomainName) + const [refreshState, setRefreshState] = useState(false) + const [genericPostRequest, postResults] = useLazyGenericPostRequestQuery() + + const onSubmit = (values) => { + const startDate = new Date() + startDate.setHours(0, 0, 0, 0) + const unixTime = Math.floor(startDate.getTime() / 1000) - 45 + const shippedValues = { + TenantFilter: tenantDomain, + Name: `CIPP Template ${tenantDomain}`, + Command: { value: `New-CIPPTemplateRun` }, + Parameters: { TemplateSettings: { ...values } }, + ScheduledTime: unixTime, + Recurrence: { value: '4h' }, + } + genericPostRequest({ + path: '/api/AddScheduledItem?DisallowDuplicateName=true', + values: shippedValues, + }).then((res) => { + setRefreshState(res.requestId) + }) + } + + const { + data: caPolicies = [], + isFetching: caIsFetching, + error: caError, + } = useListConditionalAccessPoliciesQuery({ domain: tenantDomain }) + + return ( + + <> + + + + Set Tenant as Template Library + {postResults.isFetching && ( + + )} + + } + title="Add Template Library" + icon={faEdit} + > + { + return ( + +

    + Template libraries are tenants setup to retrieve the latest version of + policies from. By setting a tenant as a template library, automatic updates + will be made to the templates within CIPP based on this template library + every 4 hours. + + Enabling this feature will overwrite templates with the same name. + +

    + + + + + {(props) => } + + + + +
    +
    + + +

    Conditional Access

    + +

    Intune

    + + + +
    +
    + {postResults.isSuccess && ( + +
  • {postResults.data.Results}
  • +
    + )} + {getResults.isFetching && ( + + Loading + + )} + {getResults.isSuccess && ( + {getResults.data?.Results} + )} + {getResults.isError && ( + + Could not connect to API: {getResults.error.message} + + )} +
    + ) + }} + /> +
    +
    +
    + +
    + ) +} + +export default TemplateLibrary diff --git a/src/views/endpoint/intune/MEMListPolicyTemplates.jsx b/src/views/endpoint/intune/MEMListPolicyTemplates.jsx index 4ca9452daeff..1b389af653bc 100644 --- a/src/views/endpoint/intune/MEMListPolicyTemplates.jsx +++ b/src/views/endpoint/intune/MEMListPolicyTemplates.jsx @@ -16,6 +16,7 @@ import { useLazyGenericGetRequestQuery } from 'src/store/api/app' import { CippPage } from 'src/components/layout' import { ModalService } from 'src/components/utilities' import CippCodeOffCanvas from 'src/components/utilities/CippCodeOffcanvas' +import { TitleButton } from 'src/components/buttons' //todo: expandable with RAWJson property. @@ -106,6 +107,11 @@ const AutopilotListTemplates = () => { Endpoint Manager Templates + {getResults.isFetching && ( diff --git a/src/views/tenant/conditional/ListCATemplates.jsx b/src/views/tenant/conditional/ListCATemplates.jsx index 555a595c3ddb..13b652854a42 100644 --- a/src/views/tenant/conditional/ListCATemplates.jsx +++ b/src/views/tenant/conditional/ListCATemplates.jsx @@ -15,6 +15,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { useLazyGenericGetRequestQuery } from 'src/store/api/app' import { CippPage } from 'src/components/layout' import { ModalService, CippCodeOffCanvas } from 'src/components/utilities' +import { TitleButton } from 'src/components/buttons' //todo: expandable with RAWJson property. @@ -87,6 +88,11 @@ const AutopilotListTemplates = () => { Results + {getResults.isFetching && ( From d0b1f4f956cfdb2e41469e9425cdf32f51933890 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Fri, 2 Aug 2024 19:28:17 +0200 Subject: [PATCH 464/475] update --- package.json | 2 +- public/version_latest.txt | 2 +- version_latest.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 169a8fbc8a05..dde03c41dba7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cipp", - "version": "6.1.1", + "version": "6.2.0", "description": "The CyberDrain Improved Partner Portal is a portal to help manage administration for Microsoft Partners.", "homepage": "https://cipp.app/", "bugs": { diff --git a/public/version_latest.txt b/public/version_latest.txt index f3b5af39e430..6abaeb2f9072 100644 --- a/public/version_latest.txt +++ b/public/version_latest.txt @@ -1 +1 @@ -6.1.1 +6.2.0 diff --git a/version_latest.txt b/version_latest.txt index f3b5af39e430..6abaeb2f9072 100644 --- a/version_latest.txt +++ b/version_latest.txt @@ -1 +1 @@ -6.1.1 +6.2.0 From 308a44c3aaa9eba264f6fdf1209c851a059811d1 Mon Sep 17 00:00:00 2001 From: jdr8 <63188001+jdr8@users.noreply.github.com> Date: Mon, 5 Aug 2024 15:34:38 +1000 Subject: [PATCH 465/475] Re-add Tenant Mode radio buttons --- .../cipp/app-settings/SettingsSuperAdmin.jsx | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/views/cipp/app-settings/SettingsSuperAdmin.jsx b/src/views/cipp/app-settings/SettingsSuperAdmin.jsx index 18cb6b397980..e6c7c8facc8e 100644 --- a/src/views/cipp/app-settings/SettingsSuperAdmin.jsx +++ b/src/views/cipp/app-settings/SettingsSuperAdmin.jsx @@ -66,6 +66,42 @@ export function SettingsSuperAdmin() {

    + + +

    Tenant Mode

    + ( + <> + {partnerConfig.isFetching && } + + + + + + + )} + /> + {webhookCreateResult.isSuccess && ( + + {webhookCreateResult?.data?.results} + + )} +
    +
    From 275641eaf24055e4110a7ae39ea9df7e5ad43a2a Mon Sep 17 00:00:00 2001 From: John Duprey Date: Mon, 5 Aug 2024 11:55:54 -0400 Subject: [PATCH 466/475] fix titlebutton link --- src/views/tenant/administration/TenantOnboardingWizard.jsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/views/tenant/administration/TenantOnboardingWizard.jsx b/src/views/tenant/administration/TenantOnboardingWizard.jsx index ebbab2a8e178..981a047702d0 100644 --- a/src/views/tenant/administration/TenantOnboardingWizard.jsx +++ b/src/views/tenant/administration/TenantOnboardingWizard.jsx @@ -136,7 +136,10 @@ const TenantOnboardingWizard = () => {
    - +
    {(props) => ( From 6f67e941479648371940a9829ef72627bd5425fb Mon Sep 17 00:00:00 2001 From: Esco Date: Tue, 6 Aug 2024 17:04:11 +0200 Subject: [PATCH 467/475] added very important T --- src/data/standards.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/data/standards.json b/src/data/standards.json index ef14f236cd1a..40380a53a9f0 100644 --- a/src/data/standards.json +++ b/src/data/standards.json @@ -93,7 +93,7 @@ "value": "default" }, { - "label": "Parial-screen background", + "label": "Partial-screen background", "value": "verticalSplit" } ] From 28d3600435f84ab999dc62bdf9c542e82a524bf5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Kj=C3=A6rg=C3=A5rd?= Date: Wed, 7 Aug 2024 18:11:52 +0200 Subject: [PATCH 468/475] Update DeletedUserRentention based on MC836942 --- src/data/standards.json | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/data/standards.json b/src/data/standards.json index 40380a53a9f0..b767bf142194 100644 --- a/src/data/standards.json +++ b/src/data/standards.json @@ -1985,14 +1985,22 @@ "name": "standards.DeletedUserRentention", "cat": "SharePoint Standards", "tag": ["lowimpact"], - "helpText": "Sets the retention period for deleted users OneDrive to the specified number of years. The default is 1 year.", - "docsDescription": "When a OneDrive user gets deleted, the personal SharePoint site is saved for selected time in years and data can be retrieved from it.", + "helpText": "Sets the retention period for deleted users OneDrive to the specified period of time. The default is 30 days.", + "docsDescription": "When a OneDrive user gets deleted, the personal SharePoint site is saved for selected amount of time that data can be retrieved from it.", "addedComponent": [ { "type": "Select", "name": "standards.DeletedUserRentention.Days", - "label": "Retention in years (Default 1)", + "label": "Retention time (Default 30 days)", "values": [ + { + "label": "30 days", + "value": "30" + }, + { + "label": "90 days", + "value": "90" + }, { "label": "1 year", "value": "365" From dc292ae69f12fdbabe37ac3da23658c35a870ae6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Kj=C3=A6rg=C3=A5rd?= Date: Thu, 8 Aug 2024 21:31:41 +0200 Subject: [PATCH 469/475] Refactor user password reset confirmation message --- src/views/identity/administration/Users.jsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/views/identity/administration/Users.jsx b/src/views/identity/administration/Users.jsx index 669e401a3611..4d236659ad7f 100644 --- a/src/views/identity/administration/Users.jsx +++ b/src/views/identity/administration/Users.jsx @@ -275,7 +275,8 @@ const Offcanvas = (row, rowIndex, formatExtraData) => { color: 'info', modal: true, modalUrl: `/api/ExecResetPass?MustChange=true&TenantFilter=${tenant.defaultDomainName}&ID=${row.id}&displayName=${row.displayName}`, - modalMessage: 'Are you sure you want to reset the password for this user?', + modalMessage: + 'Are you sure you want to reset the password for this user? The user must change their password at next logon.', }, { label: 'Reset Password', @@ -285,11 +286,11 @@ const Offcanvas = (row, rowIndex, formatExtraData) => { modalMessage: 'Are you sure you want to reset the password for this user?', }, { - label: 'Preprovision OneDrive', + label: 'Pre-provision OneDrive', color: 'info', modal: true, modalUrl: `/api/ExecOneDriveProvision?TenantFilter=${tenant.defaultDomainName}&UserPrincipalName=${row.userPrincipalName}`, - modalMessage: 'Are you sure you want to preprovision onedrive for this user??', + modalMessage: 'Are you sure you want to pre-provision OneDrive for this user??', }, { label: 'Clear ImmutableId', From 6a77f2b58c81ca323b763e71e1ee407a773a0104 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 8 Aug 2024 15:55:35 -0400 Subject: [PATCH 470/475] Add confirmation dialog to save standards --- .../tenant/standards/ListAppliedStandards.jsx | 37 ++++++++++++------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/src/views/tenant/standards/ListAppliedStandards.jsx b/src/views/tenant/standards/ListAppliedStandards.jsx index 10eab27c96fd..226f2cf028c6 100644 --- a/src/views/tenant/standards/ListAppliedStandards.jsx +++ b/src/views/tenant/standards/ListAppliedStandards.jsx @@ -76,6 +76,7 @@ const DeleteAction = () => { ) } + const ApplyNewStandard = () => { const [templateStandard, setTemplateStandard] = useState() const [loadedTemplate, setLoadedTemplate] = useState(false) @@ -268,6 +269,7 @@ const ApplyNewStandard = () => { }) const tenantDomain = useSelector((state) => state.app.currentTenant.defaultDomainName) + const tenantDisplayName = useSelector((state) => state.app.currentTenant.displayName) //console.log('tenantDomain', tenantDomain) const [genericPostRequest, postResults] = useLazyGenericPostRequestQuery() @@ -294,20 +296,27 @@ const ApplyNewStandard = () => { }) const handleSubmit = async (values) => { - Object.keys(values.standards).filter(function (x) { - if (values.standards[x] === false) { - delete values.standards[x] - } - return null - }) - - //filter on only objects that are 'true' - genericPostRequest({ - path: '/api/AddStandardsDeploy', - values: { ...values.standards, tenant: tenantDomain }, - }).then(() => { - refetchStandards() - refetchConsolidated() + ModalService.confirm({ + title: 'Save Standards', + body: ( +
    +

    + Are you sure you want to save these standards to {tenantDisplayName}? This will apply + all Remediate options on the next run. +

    +
    + ), + confirmLabel: 'Save', + cancelLabel: 'Cancel', + onConfirm: () => { + genericPostRequest({ + path: '/api/AddStandardsDeploy', + values: { ...values.standards, tenant: tenantDomain }, + }).then(() => { + refetchStandards() + refetchConsolidated() + }) + }, }) } const [intuneGetRequest, intuneTemplates] = useLazyGenericGetRequestQuery() From 5739fc4e144952a3deb617436d2e3141a8ae50fe Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 8 Aug 2024 15:57:49 -0400 Subject: [PATCH 471/475] fix link --- src/views/tenant/administration/GDAPRoleWizard.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/tenant/administration/GDAPRoleWizard.jsx b/src/views/tenant/administration/GDAPRoleWizard.jsx index acaca41de84a..8607617cfb0a 100644 --- a/src/views/tenant/administration/GDAPRoleWizard.jsx +++ b/src/views/tenant/administration/GDAPRoleWizard.jsx @@ -168,7 +168,7 @@ const GDAPRoleWizard = () => { return
  • {message}
  • })} - + Create GDAP Invite From 28a551d709a5a0f1597336ee50bdcd5c8e13fccd Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 8 Aug 2024 16:30:20 -0400 Subject: [PATCH 472/475] Up version --- package.json | 2 +- public/version_latest.txt | 2 +- version_latest.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index dde03c41dba7..9e3b09fd6c58 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cipp", - "version": "6.2.0", + "version": "6.2.1", "description": "The CyberDrain Improved Partner Portal is a portal to help manage administration for Microsoft Partners.", "homepage": "https://cipp.app/", "bugs": { diff --git a/public/version_latest.txt b/public/version_latest.txt index 6abaeb2f9072..024b066c0bb7 100644 --- a/public/version_latest.txt +++ b/public/version_latest.txt @@ -1 +1 @@ -6.2.0 +6.2.1 diff --git a/version_latest.txt b/version_latest.txt index 6abaeb2f9072..024b066c0bb7 100644 --- a/version_latest.txt +++ b/version_latest.txt @@ -1 +1 @@ -6.2.0 +6.2.1 From 1e89c9843a48630408763b7c363c34cf506aa3f0 Mon Sep 17 00:00:00 2001 From: Ryan Kempt Date: Thu, 8 Aug 2024 23:18:21 -0400 Subject: [PATCH 473/475] add missing country codes --- src/data/AuditLogSchema.json | 3 +++ src/data/countryList.json | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/data/AuditLogSchema.json b/src/data/AuditLogSchema.json index c05a1187386b..35530d0fb583 100644 --- a/src/data/AuditLogSchema.json +++ b/src/data/AuditLogSchema.json @@ -731,6 +731,7 @@ { "value": "AR", "name": "Argentina" }, { "value": "AM", "name": "Armenia" }, { "value": "AW", "name": "Aruba" }, + { "value": "AC", "name": "Ascension Island" }, { "value": "AU", "name": "Australia" }, { "value": "AT", "name": "Austria" }, { "value": "AZ", "name": "Azerbaijan" }, @@ -779,6 +780,7 @@ { "value": "CY", "name": "Cyprus" }, { "value": "CZ", "name": "Czech Republic" }, { "value": "DK", "name": "Denmark" }, + { "value": "DG", "name": "Diego Garcia" }, { "value": "DJ", "name": "Djibouti" }, { "value": "DM", "name": "Dominica" }, { "value": "DO", "name": "Dominican Republic" }, @@ -837,6 +839,7 @@ { "value": "KI", "name": "Kiribati" }, { "value": "KP", "name": "Korea, Democratic People's Republic of" }, { "value": "KR", "name": "Korea, Republic of" }, + { "value": "XK", "name": "Kosovo" }, { "value": "KW", "name": "Kuwait" }, { "value": "KG", "name": "Kyrgyzstan" }, { "value": "LA", "name": "Lao People's Democratic Republic" }, diff --git a/src/data/countryList.json b/src/data/countryList.json index 9db31e21192d..9595ba9f517c 100644 --- a/src/data/countryList.json +++ b/src/data/countryList.json @@ -12,6 +12,7 @@ { "Code": "AR", "Name": "Argentina" }, { "Code": "AM", "Name": "Armenia" }, { "Code": "AW", "Name": "Aruba" }, + { "Code": "AC", "Name": "Ascension Island" }, { "Code": "AU", "Name": "Australia" }, { "Code": "AT", "Name": "Austria" }, { "Code": "AZ", "Name": "Azerbaijan" }, @@ -60,6 +61,7 @@ { "Code": "CY", "Name": "Cyprus" }, { "Code": "CZ", "Name": "Czech Republic" }, { "Code": "DK", "Name": "Denmark" }, + { "Code": "DG", "Name": "Diego Garcia" }, { "Code": "DJ", "Name": "Djibouti" }, { "Code": "DM", "Name": "Dominica" }, { "Code": "DO", "Name": "Dominican Republic" }, @@ -118,6 +120,7 @@ { "Code": "KI", "Name": "Kiribati" }, { "Code": "KP", "Name": "Korea, Democratic People's Republic of" }, { "Code": "KR", "Name": "Korea, Republic of" }, + { "Code": "XK", "Name": "Kosovo" }, { "Code": "KW", "Name": "Kuwait" }, { "Code": "KG", "Name": "Kyrgyzstan" }, { "Code": "LA", "Name": "Lao People's Democratic Republic" }, From e08e8752d7c27fec7fb72003fe70b2b4813e0ad7 Mon Sep 17 00:00:00 2001 From: isgq-github01 <159874617+isgq-github01@users.noreply.github.com> Date: Mon, 12 Aug 2024 09:49:12 +1000 Subject: [PATCH 474/475] Update standards.json - fixed typo Fixed "restirct" typo --- src/data/standards.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/data/standards.json b/src/data/standards.json index b767bf142194..45e7c0438ea1 100644 --- a/src/data/standards.json +++ b/src/data/standards.json @@ -2341,7 +2341,7 @@ "value": "none" }, { - "label": "Restirct sharing to specific domains", + "label": "Restrict sharing to specific domains", "value": "allowList" }, { From 7ee823bc9077524cac9928fe374f39385e545ecb Mon Sep 17 00:00:00 2001 From: John Duprey Date: Mon, 12 Aug 2024 11:18:58 -0400 Subject: [PATCH 475/475] Up version --- package-lock.json | 4 ++-- package.json | 2 +- public/version_latest.txt | 2 +- version_latest.txt | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5148294a75a6..52097c7d8d38 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "cipp", - "version": "6.1.1", + "version": "6.2.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "cipp", - "version": "6.1.1", + "version": "6.2.2", "license": "AGPL-3.0", "dependencies": { "@coreui/chartjs": "^3.0.0", diff --git a/package.json b/package.json index 9e3b09fd6c58..41b4ceedb492 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cipp", - "version": "6.2.1", + "version": "6.2.2", "description": "The CyberDrain Improved Partner Portal is a portal to help manage administration for Microsoft Partners.", "homepage": "https://cipp.app/", "bugs": { diff --git a/public/version_latest.txt b/public/version_latest.txt index 024b066c0bb7..ca06394388d6 100644 --- a/public/version_latest.txt +++ b/public/version_latest.txt @@ -1 +1 @@ -6.2.1 +6.2.2 diff --git a/version_latest.txt b/version_latest.txt index 024b066c0bb7..ca06394388d6 100644 --- a/version_latest.txt +++ b/version_latest.txt @@ -1 +1 @@ -6.2.1 +6.2.2