From 87ae2ea7c29d4aed1e144859bad389f576338048 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Fri, 12 Apr 2024 14:39:52 -0400 Subject: [PATCH 001/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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/536] 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 From aff91489a98aa71e7ea7cdfad5d41203604e14aa Mon Sep 17 00:00:00 2001 From: John Duprey Date: Tue, 13 Aug 2024 22:47:48 -0400 Subject: [PATCH 476/536] Add processor function support Expects CIPP-API-Processor as folder name --- Tools/Start-CippDevEmulators.ps1 | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/Tools/Start-CippDevEmulators.ps1 b/Tools/Start-CippDevEmulators.ps1 index b4f6ca696ffe..bde7511caac1 100644 --- a/Tools/Start-CippDevEmulators.ps1 +++ b/Tools/Start-CippDevEmulators.ps1 @@ -1,4 +1,12 @@ -Write-Host "Starting CIPP Dev Emulators" +Write-Host 'Starting CIPP Dev Emulators' +Get-Process node -ErrorAction SilentlyContinue | Stop-Process -ErrorAction SilentlyContinue $Path = (Get-Item $PSScriptRoot).Parent.Parent.FullName -wt --title CIPP`; new-tab --title 'Azurite' -d $Path pwsh -c azurite`; new-tab --title 'FunctionApp' -d $Path\CIPP-API pwsh -c func start`; new-tab --title 'CIPP Frontend' -d $Path\CIPP pwsh -c npm run start`; new-tab --title 'SWA' -d $Path\CIPP pwsh -c npm run start-swa + +$Process = Read-Host -Prompt 'Start Process Function (y/N)?' + +if ($Process -eq 'y') { + wt --title CIPP`; new-tab --title 'Azurite' -d $Path pwsh -c azurite`; new-tab --title 'FunctionApp' -d $Path\CIPP-API pwsh -c func start`; new-tab --title 'CIPP Frontend' -d $Path\CIPP pwsh -c npm run start`; new-tab --title 'SWA' -d $Path\CIPP pwsh -c npm run start-swa`; new-tab --title 'CIPP-API-Processor' -d $Path\CIPP-API-Processor pwsh -c func start --port 7072 +} else { + wt --title CIPP`; new-tab --title 'Azurite' -d $Path pwsh -c azurite`; new-tab --title 'FunctionApp' -d $Path\CIPP-API pwsh -c func start`; new-tab --title 'CIPP Frontend' -d $Path\CIPP pwsh -c npm run start`; new-tab --title 'SWA' -d $Path\CIPP pwsh -c npm run start-swa +} From a7308f45f15892dba954ba834c744d15e92f788e Mon Sep 17 00:00:00 2001 From: John Duprey Date: Tue, 13 Aug 2024 22:50:29 -0400 Subject: [PATCH 477/536] Add extension call to action support --- src/views/cipp/Extensions.jsx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/views/cipp/Extensions.jsx b/src/views/cipp/Extensions.jsx index 5923599a6f66..84841ec3ec18 100644 --- a/src/views/cipp/Extensions.jsx +++ b/src/views/cipp/Extensions.jsx @@ -22,6 +22,7 @@ import CippButtonCard from 'src/components/contentcards/CippButtonCard.jsx' import { RFFCFormInput, RFFCFormSwitch } from 'src/components/forms/RFFComponents.jsx' import { Form } from 'react-final-form' import ExtensionMappings from 'src/views/cipp/ExtensionMappings.jsx' +import ReactHtmlParser from 'react-html-parser' export default function CIPPExtensions() { const [listBackend, listBackendResult] = useLazyGenericGetRequestQuery() @@ -121,6 +122,12 @@ export default function CIPPExtensions() { clients. )} + {integration?.callToAction && ( + + + {ReactHtmlParser(integration.callToAction)} + + )} Date: Tue, 13 Aug 2024 23:24:57 -0400 Subject: [PATCH 478/536] CIPP-SAM API Permissions - Add new components - SettingsAppPermissions - CippAppPermissionBuilder - Modify CippButtonCard, make isFetching not required and default false - Modify CellTip & CellGenericFormat, allow for wrapping text - Add onCreateOption to Creatable - CSS tweaks - Make adjust CippTable filter z-index to not conflict with select search - Add CIPP-SAM permission SuperAdmin section --- .../contentcards/CippButtonCard.jsx | 4 +- src/components/forms/RFFComponents.jsx | 6 +- src/components/tables/CellGenericFormat.jsx | 12 +- src/components/tables/CellTip.jsx | 4 +- .../utilities/CippAppPermissionBuilder.jsx | 585 ++++++++++++++++++ src/scss/_custom.scss | 6 + src/scss/_themes.scss | 1 + .../cipp/app-settings/SettingsSuperAdmin.jsx | 212 +++++-- .../components/SettingsAppPermissions.jsx | 65 ++ 9 files changed, 821 insertions(+), 74 deletions(-) create mode 100644 src/components/utilities/CippAppPermissionBuilder.jsx create mode 100644 src/views/cipp/app-settings/components/SettingsAppPermissions.jsx diff --git a/src/components/contentcards/CippButtonCard.jsx b/src/components/contentcards/CippButtonCard.jsx index 8e74d5470693..8e280ab067d3 100644 --- a/src/components/contentcards/CippButtonCard.jsx +++ b/src/components/contentcards/CippButtonCard.jsx @@ -8,7 +8,7 @@ export default function CippButtonCard({ titleType = 'normal', CardButton, children, - isFetching, + isFetching = false, className = 'h-100', }) { return ( @@ -32,6 +32,6 @@ CippButtonCard.propTypes = { titleType: PropTypes.string, CardButton: PropTypes.element.isRequired, children: PropTypes.element.isRequired, - isFetching: PropTypes.bool.isRequired, + isFetching: PropTypes.bool, className: PropTypes.string, } diff --git a/src/components/forms/RFFComponents.jsx b/src/components/forms/RFFComponents.jsx index 14163a033d62..30e096a71aa8 100644 --- a/src/components/forms/RFFComponents.jsx +++ b/src/components/forms/RFFComponents.jsx @@ -516,6 +516,7 @@ export const RFFSelectSearch = ({ retainInput = true, isLoading = false, allowCreate = false, + onCreateOption, refreshFunction, ...props }) => { @@ -589,7 +590,7 @@ export const RFFSelectSearch = ({ )} {allowCreate ? ( - + ) : ( - {dropMessage} - -
    - + <> + {returnCard ? ( + +
    + + + {dropMessage} + +
    +
    + ) : ( +
    + + + {dropMessage} + +
    + )} + ) } @@ -73,6 +92,7 @@ CippDropzone.propTypes = { dropMessage: PropTypes.string, accept: PropTypes.object, maxFiles: PropTypes.number, + returnCard: PropTypes.bool, } export default CippDropzone From 7e87b2a1cdde1d23a885f31aa829bbbf02966ad5 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Wed, 14 Aug 2024 16:09:56 -0400 Subject: [PATCH 484/536] Create service principal capabilities --- .../utilities/CippAppPermissionBuilder.jsx | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/components/utilities/CippAppPermissionBuilder.jsx b/src/components/utilities/CippAppPermissionBuilder.jsx index 93b1b9abef8c..e4c2ee3bfdf8 100644 --- a/src/components/utilities/CippAppPermissionBuilder.jsx +++ b/src/components/utilities/CippAppPermissionBuilder.jsx @@ -13,7 +13,7 @@ import { } 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 { useGenericGetRequestQuery, useLazyGenericGetRequestQuery } from 'src/store/api/app' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { TenantSelectorMultiple, @@ -53,6 +53,8 @@ const CippAppPermissionBuilder = ({ onSubmit, currentPermissions = {}, isSubmitt path: 'api/ExecServicePrincipals', }) + const [createServicePrincipal, createResult] = useLazyGenericGetRequestQuery() + const removeServicePrincipal = (appId) => { var servicePrincipal = selectedApp.find((sp) => sp?.appId === appId) var newServicePrincipals = selectedApp.filter((sp) => sp?.appId !== appId) @@ -91,6 +93,15 @@ const CippAppPermissionBuilder = ({ onSubmit, currentPermissions = {}, isSubmitt } } + const onCreateServicePrincipal = (appId) => { + createServicePrincipal({ + path: 'api/ExecServicePrincipals?Action=Create&AppId=' + appId, + }).then(() => { + refetchSpList() + setCalloutMessage(createResult?.data?.Results) + }) + } + const addPermissionRow = (servicePrincipal, permissionType, permission) => { var updatedPermissions = JSON.parse(JSON.stringify(newPermissions)) @@ -683,7 +694,7 @@ const CippAppPermissionBuilder = ({ onSubmit, currentPermissions = {}, isSubmitt refreshFunction={() => refetchSpList()} allowCreate={true} onCreateOption={(newSp) => { - console.log(newSp) + onCreateServicePrincipal(newSp) }} placeholder="(Advanced) Select a Service Principal" /> From f22f756ed8dec5115bf0c12bf232b3bc7df9a36b Mon Sep 17 00:00:00 2001 From: John Duprey Date: Wed, 14 Aug 2024 18:39:29 -0400 Subject: [PATCH 485/536] Permission builder --- .../utilities/CippAppPermissionBuilder.jsx | 79 +++++++++++++++++++ .../components/SettingsAppPermissions.jsx | 13 ++- 2 files changed, 88 insertions(+), 4 deletions(-) diff --git a/src/components/utilities/CippAppPermissionBuilder.jsx b/src/components/utilities/CippAppPermissionBuilder.jsx index e4c2ee3bfdf8..c986b67145f2 100644 --- a/src/components/utilities/CippAppPermissionBuilder.jsx +++ b/src/components/utilities/CippAppPermissionBuilder.jsx @@ -821,6 +821,85 @@ const CippAppPermissionBuilder = ({ onSubmit, currentPermissions = {}, isSubmitt )} + + {newPermissions?.MissingPermissions && + newPermissions?.Type === 'Table' && + Object.keys(newPermissions?.MissingPermissions).length > 0 && ( + + + + + + + New Permissions Available + {Object.keys(newPermissions?.MissingPermissions).map((perm) => { + // translate appid to display name + var sp = servicePrincipals?.Results?.find( + (sp) => sp.appId === perm, + ) + return ( +
    + {sp?.displayName}:{' '} + {Object.keys(newPermissions?.MissingPermissions[perm]).map( + (type) => { + return ( + <> + {newPermissions?.MissingPermissions[perm][type] + .length > 0 && ( + + {type == 'applicationPermissions' + ? 'Application' + : 'Delegated'}{' '} + -{' '} + {newPermissions?.MissingPermissions[perm][type] + .map((p) => { + return p.value + }) + .join(', ')} + + )} + + ) + }, + )} +
    + ) + })} +
    + + + { + var updatedPermissions = JSON.parse( + JSON.stringify(newPermissions), + ) + Object.keys(newPermissions?.MissingPermissions).map( + (perm) => { + Object.keys( + newPermissions?.MissingPermissions[perm], + ).map((type) => { + newPermissions?.MissingPermissions[perm][type].map( + (p) => { + updatedPermissions.Permissions[perm][type].push(p) + }, + ) + }) + }, + ) + updatedPermissions.MissingPermissions = {} + setNewPermissions(updatedPermissions) + }} + className={`circular-button float-end`} + > + + + + +
    +
    +
    +
    + )} <> {selectedApp?.length > 0 && diff --git a/src/views/cipp/app-settings/components/SettingsAppPermissions.jsx b/src/views/cipp/app-settings/components/SettingsAppPermissions.jsx index c3b6da3ffbd7..d36dffb0b597 100644 --- a/src/views/cipp/app-settings/components/SettingsAppPermissions.jsx +++ b/src/views/cipp/app-settings/components/SettingsAppPermissions.jsx @@ -28,13 +28,18 @@ const SettingsAppPermissions = () => { genericPostRequest({ path: 'api/ExecSAMAppPermissions?Action=Update', values: values, + }).then(() => { + refetchSam() }) } - const { data: samAppPermissions = [], isFetching: samAppPermissionsFetching } = - useGenericGetRequestQuery({ - path: 'api/ExecSAMAppPermissions', - }) + const { + data: samAppPermissions = [], + isFetching: samAppPermissionsFetching, + refetch: refetchSam, + } = useGenericGetRequestQuery({ + path: 'api/ExecSAMAppPermissions', + }) return ( From d642177ad73c52b5f621de97377161f70108c2aa Mon Sep 17 00:00:00 2001 From: John Duprey Date: Fri, 16 Aug 2024 12:17:38 -0400 Subject: [PATCH 486/536] Fix CellTable item count for single objects --- src/components/tables/CellTable.jsx | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/components/tables/CellTable.jsx b/src/components/tables/CellTable.jsx index cfcbd5403065..2186a406fe6b 100644 --- a/src/components/tables/CellTable.jsx +++ b/src/components/tables/CellTable.jsx @@ -23,6 +23,15 @@ export default function cellTable( if (columnProp === undefined || columnProp === null) { columnProp = [] } else { + var objectLength = 1 + var lengthText = 'Item' + if (columnProp instanceof Array) { + objectLength = columnProp.length + if (objectLength > 1) { + lengthText = 'Items' + } + } + if (!Array.isArray(columnProp) && typeof columnProp === 'object') { columnProp = Object.keys(columnProp).map((key) => { return { @@ -93,7 +102,7 @@ export default function cellTable( size="sm" onClick={() => handleTable({ columnProp })} > - {columnProp.length} Items + {objectLength} {lengthText} ) } From 22c72644a9da76cffcc9c2d4a8e1c1725eddc824 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Fri, 16 Aug 2024 17:29:11 -0400 Subject: [PATCH 487/536] Huntress RogueApps - Add new scripted alert - Update alert wizard to accept a description property for scripted alerts --- src/data/alerts.json | 6 +++++ .../tenant/administration/AlertWizard.jsx | 24 +++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/src/data/alerts.json b/src/data/alerts.json index 835216740c4d..13f18265caf4 100644 --- a/src/data/alerts.json +++ b/src/data/alerts.json @@ -99,5 +99,11 @@ "name": "DeviceCompliance", "label": "Alert on device compliance issues", "recommendedRunInterval": "4h" + }, + { + "name": "HuntressRogueApps", + "label": "Alert on Huntress Rogue Apps detected", + "recommendedRunInterval": "4h", + "description": "Huntress has provided a repository of known rogue apps that are commonly used in BEC, data exfiltration and other Microsoft 365 attacks. This alert will notify you if any of these apps are detected in the selected tenant(s). For more information, see https://huntresslabs.github.io/rogueapps/." } ] diff --git a/src/views/tenant/administration/AlertWizard.jsx b/src/views/tenant/administration/AlertWizard.jsx index 9b5253ef7351..e444c11ec123 100644 --- a/src/views/tenant/administration/AlertWizard.jsx +++ b/src/views/tenant/administration/AlertWizard.jsx @@ -21,6 +21,7 @@ import auditLogSchema from 'src/data/AuditLogSchema.json' import auditLogTemplates from 'src/data/AuditLogTemplates.json' import Skeleton from 'react-loading-skeleton' import { required } from 'src/validators' +import HtmlParser from 'react-html-parser' const AlertWizard = () => { const tenantDomain = useSelector((state) => state.app.currentTenant.defaultDomainName) @@ -112,6 +113,18 @@ const AlertWizard = () => { } } + const getScriptDescription = () => { + const values = currentFormState?.values + if (values) { + const command = values.command?.value + if (command?.description) { + return HtmlParser(command.description) + } else { + return null + } + } + } + const setAuditForm = (e) => { const preset = presetValues.find((p) => p.value === e.value) setAuditFormState(preset.template) @@ -368,6 +381,16 @@ const AlertWizard = () => { render={({ handleSubmit, submitting, values }) => { return ( + {getScriptDescription() && ( + + + + + {getScriptDescription()} + + + + )} { /> + From b86f0dd65321e28ff61a0752147fc2595c024d59 Mon Sep 17 00:00:00 2001 From: Chris Brannon Date: Sat, 17 Aug 2024 17:03:54 -0400 Subject: [PATCH 488/536] Assign & Unassign Teams Voice User Numbers --- src/views/teams-share/teams/BusinessVoice.jsx | 249 ++++++++++++++---- 1 file changed, 194 insertions(+), 55 deletions(-) diff --git a/src/views/teams-share/teams/BusinessVoice.jsx b/src/views/teams-share/teams/BusinessVoice.jsx index 6c132806d8bf..ada434df6895 100644 --- a/src/views/teams-share/teams/BusinessVoice.jsx +++ b/src/views/teams-share/teams/BusinessVoice.jsx @@ -1,68 +1,207 @@ -import React from 'react' +import React, { useState } from 'react' +import { CButton } from '@coreui/react' import { useSelector } from 'react-redux' import { CellBoolean } from 'src/components/tables' import { CippPageList } from 'src/components/layout' - -const Formatter = (cell) => CellBoolean({ cell }) -const columns = [ - { - name: 'Assigned to User', - selector: (row) => row['AssignedTo'], - sortable: true, - exportSelector: 'AssignedTo', - }, - { - name: 'Phone Number', - selector: (row) => row['TelephoneNumber'], - sortable: true, - exportSelector: 'TelephoneNumber', - }, - { - name: 'Number Type', - selector: (row) => row['NumberType'], - sortable: true, - exportSelector: 'NumberType', - }, - { - name: 'Country', - selector: (row) => row['IsoCountryCode'], - sortable: true, - exportSelector: 'IsCountryCode', - }, - { - name: 'Location', - selector: (row) => row['PlaceName'], - sortable: true, - exportSelector: 'PlaceName', - }, - { - name: 'Activation State', - selector: (row) => row['ActivationState'], - formatter: Formatter, - exportSelector: 'ActivationState', - sortable: true, - }, - { - name: 'Operator Connect', - selector: (row) => row['IsOperatorConnect'], - formatter: Formatter, - sortable: true, - exportSelector: 'IsOperatorConnect', - }, - { - name: 'Purchased on', - selector: (row) => row['AcquisitionDate'], - sortable: true, - exportSelector: 'AcquisitionDate', - }, -] +import { TitleButton } from 'src/components/buttons' +import { CippActionsOffcanvas } from 'src/components/utilities' +import { faEllipsisV } from '@fortawesome/free-solid-svg-icons' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' const BusinessVoice = () => { const tenant = useSelector((state) => state.app.currentTenant) + const Offcanvas = (row, rowIndex, formatExtraData) => { + const tenant = useSelector((state) => state.app.currentTenant) + const [ocVisible, setOCVisible] = useState(false) + return ( + <> + setOCVisible(true)}> + + + setOCVisible(false)} + /> + + ) + } + const Formatter = (cell) => CellBoolean({ cell }) + const usageFormatter = (cell) => { + if (cell.includes('UserAssignment')) { + return 'User' + } + if (cell.includes('FirstPartyAppAssignment')) { + return 'Voice App' + } + if (cell.includes('ConferenceAssignment')) { + return 'Conference' + } + return cell[0] + } + const columns = [ + { + name: 'Assigned to User', + selector: (row) => row['AssignedTo'], + sortable: true, + exportSelector: 'AssignedTo', + }, + { + name: 'Phone Number', + selector: (row) => row['TelephoneNumber'], + sortable: true, + exportSelector: 'TelephoneNumber', + }, + { + name: 'Assignment Status', + selector: (row) => row['AssignmentStatus'], + sortable: true, + exportSelector: 'AssignmentStatus', + }, + { + name: 'Number Type', + selector: (row) => row['NumberType'], + sortable: true, + exportSelector: 'NumberType', + }, + { + name: 'Licensed Usage', + selector: (row) => usageFormatter(row['AcquiredCapabilities']), + sortable: true, + exportSelector: 'AcquiredCapabilities', + }, + { + name: 'Country', + selector: (row) => row['IsoCountryCode'], + sortable: true, + exportSelector: 'IsCountryCode', + }, + { + name: 'Location', + selector: (row) => row['PlaceName'], + sortable: true, + exportSelector: 'PlaceName', + }, + { + name: 'Activation State', + selector: (row) => row['ActivationState'], + formatter: Formatter, + exportSelector: 'ActivationState', + sortable: true, + }, + { + name: 'Operator Connect', + selector: (row) => row['IsOperatorConnect'], + formatter: Formatter, + sortable: true, + exportSelector: 'IsOperatorConnect', + }, + { + name: 'Purchased on', + selector: (row) => row['AcquisitionDate'], + sortable: true, + exportSelector: 'AcquisitionDate', + }, + { + name: 'Actions', + cell: Offcanvas, + }, + ] + const titleButtons = ( +
    +
    + +
    +
    + ) return ( Date: Sat, 17 Aug 2024 17:26:25 -0400 Subject: [PATCH 489/536] Formatting Issue --- src/views/teams-share/teams/BusinessVoice.jsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/views/teams-share/teams/BusinessVoice.jsx b/src/views/teams-share/teams/BusinessVoice.jsx index ada434df6895..5f2c0d32598a 100644 --- a/src/views/teams-share/teams/BusinessVoice.jsx +++ b/src/views/teams-share/teams/BusinessVoice.jsx @@ -199,7 +199,8 @@ const BusinessVoice = () => { filterlist: [ { filterName: 'Unassigned User Numbers', - filter: 'Complex: AssignmentStatus eq Unassigned; AcquiredCapabilities like UserAssignment', + filter: + 'Complex: AssignmentStatus eq Unassigned; AcquiredCapabilities like UserAssignment', }, ], columns, From cc0eda1b7ac6c105c1ae5f70b9a817102117b88e Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Sun, 18 Aug 2024 18:29:48 +0200 Subject: [PATCH 490/536] Fixes jitadmin issues --- src/views/identity/administration/DeployJITAdmin.jsx | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/views/identity/administration/DeployJITAdmin.jsx b/src/views/identity/administration/DeployJITAdmin.jsx index 81d23666fa61..146ab7d6fef1 100644 --- a/src/views/identity/administration/DeployJITAdmin.jsx +++ b/src/views/identity/administration/DeployJITAdmin.jsx @@ -47,10 +47,13 @@ const DeployJITAdmin = () => { const onSubmit = (values) => { const startTime = Math.floor(startDate.getTime() / 1000) const endTime = Math.floor(endDate.getTime() / 1000) + const shippedValues = { TenantFilter: tenantDomain, - UserId: values.UserId?.value, - UserPrincipalName: `${values.username}@${values.domain}`, + UserId: values.UserId?.value.id, + UserPrincipalName: values.username + ? `${values.username}@${values.domain}` + : values.UserId?.value.userPrincipalName, FirstName: values.FirstName, LastName: values.LastName, useraction: values.useraction, @@ -168,7 +171,7 @@ const DeployJITAdmin = () => { ({ - value: user.id, + value: { userPrincipalName: user.userPrincipalName, id: user.id }, name: `${user.displayName} <${user.userPrincipalName}>`, }))} placeholder={!usersIsFetching ? 'Select user' : 'Loading...'} From e6258b92973e6ca8c9fc7ead4f1852f476d81682 Mon Sep 17 00:00:00 2001 From: MoltenTesseract Date: Mon, 19 Aug 2024 14:44:09 +1000 Subject: [PATCH 491/536] Fix delete rule confirmation text The delete popup incorrectly asked to confirm to disable the rule, not delete it. --- src/views/email-exchange/connectors/ConnectorList.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/email-exchange/connectors/ConnectorList.jsx b/src/views/email-exchange/connectors/ConnectorList.jsx index c18d5eeb0004..1f3829abcb1d 100644 --- a/src/views/email-exchange/connectors/ConnectorList.jsx +++ b/src/views/email-exchange/connectors/ConnectorList.jsx @@ -57,7 +57,7 @@ const Offcanvas = (row, rowIndex, formatExtraData) => { modal: true, icon: , modalUrl: `/api/RemoveExConnector?TenantFilter=${tenant.defaultDomainName}&GUID=${row.Guid}&Type=${row.cippconnectortype}`, - modalMessage: 'Are you sure you want to disable this rule?', + modalMessage: 'Are you sure you want to delete this rule?', }, ]} placement="end" From aaa50b96f810c51e2f7c3fa9ba167ce201166682 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Mon, 19 Aug 2024 18:58:09 +0200 Subject: [PATCH 492/536] 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 41b4ceedb492..b74d149fe82e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cipp", - "version": "6.2.2", + "version": "6.3.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 ca06394388d6..798e38995c4d 100644 --- a/public/version_latest.txt +++ b/public/version_latest.txt @@ -1 +1 @@ -6.2.2 +6.3.0 diff --git a/version_latest.txt b/version_latest.txt index ca06394388d6..798e38995c4d 100644 --- a/version_latest.txt +++ b/version_latest.txt @@ -1 +1 @@ -6.2.2 +6.3.0 From b3d79e60f23a239c9c049a3d79d4a5240854af58 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Mon, 19 Aug 2024 13:16:47 -0400 Subject: [PATCH 493/536] Fix reset mfa --- src/views/identity/administration/Users.jsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/views/identity/administration/Users.jsx b/src/views/identity/administration/Users.jsx index 4d236659ad7f..eb0cd4f29f6c 100644 --- a/src/views/identity/administration/Users.jsx +++ b/src/views/identity/administration/Users.jsx @@ -99,10 +99,10 @@ const Offcanvas = (row, rowIndex, formatExtraData) => { }, { label: 'Rerequire MFA registration', - link: entraLink, color: 'info', - target: '_blank', - external: true, + modal: true, + modalUrl: `/api/ExecResetMFA?TenantFilter=${tenant.defaultDomainName}&ID=${row.userPrincipalName}`, + modalMessage: 'Are you sure you want to reset MFA for this user?', }, { label: 'Send MFA Push', @@ -527,8 +527,8 @@ const Users = (row) => { { label: 'Rerequire MFA registration', modal: true, - modalUrl: `/api/ExecResetMFA?TenantFilter=!Tenant&ID=!id`, - modalMessage: 'Are you sure you want to enable MFA for these users?', + modalUrl: `/api/ExecResetMFA?TenantFilter=!Tenant&ID=!userPrincipalName`, + modalMessage: 'Are you sure you want to reset MFA for these users?', }, { label: 'Set Per-User MFA', From c880aad889c50615269d8cccb1e63e6e17b3c6d8 Mon Sep 17 00:00:00 2001 From: MoltenTesseract Date: Tue, 20 Aug 2024 09:33:27 +1000 Subject: [PATCH 494/536] Delete exchange rule confirmation fixed Delete exchange rule confirmation text still said disable rule instead of delete. --- src/views/email-exchange/transport/TransportRules.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/email-exchange/transport/TransportRules.jsx b/src/views/email-exchange/transport/TransportRules.jsx index 9d534e17dee5..549f245a2ea5 100644 --- a/src/views/email-exchange/transport/TransportRules.jsx +++ b/src/views/email-exchange/transport/TransportRules.jsx @@ -57,7 +57,7 @@ const Offcanvas = (row, rowIndex, formatExtraData) => { modal: true, icon: , modalUrl: `/api/RemoveTransportRule?TenantFilter=${tenant.defaultDomainName}&GUID=${row.Guid}`, - modalMessage: 'Are you sure you want to disable this rule?', + modalMessage: 'Are you sure you want to delete this rule?', }, ]} placement="end" From a00ba2fd7083336e224f10895f58eb61070caaf5 Mon Sep 17 00:00:00 2001 From: MoltenTesseract Date: Tue, 20 Aug 2024 12:40:24 +1000 Subject: [PATCH 495/536] Fix flyout extended information and incorrect wording Flyout extended information was pulling the incorrect columns, so all info was showing as 'undefined' . Updated all references to "rule" and replaced with "connector". --- .../connectors/ConnectorList.jsx | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/views/email-exchange/connectors/ConnectorList.jsx b/src/views/email-exchange/connectors/ConnectorList.jsx index 1f3829abcb1d..c5c2b2d0951a 100644 --- a/src/views/email-exchange/connectors/ConnectorList.jsx +++ b/src/views/email-exchange/connectors/ConnectorList.jsx @@ -20,44 +20,46 @@ const Offcanvas = (row, rowIndex, formatExtraData) => { , modalBody: row, modalType: 'POST', modalUrl: `/api/AddExConnectorTemplate`, - modalMessage: 'Are you sure you want to create a template based on this rule?', + modalMessage: 'Are you sure you want to create a template based on this connector?', }, { - label: 'Enable Rule', + label: 'Enable Connector', color: 'info', icon: , modal: true, modalUrl: `/api/EditExConnector?State=Enable&TenantFilter=${tenant.defaultDomainName}&GUID=${row.Guid}&Type=${row.cippconnectortype}`, - modalMessage: 'Are you sure you want to enable this rule?', + modalMessage: 'Are you sure you want to enable this connector?', }, { - label: 'Disable Rule', + label: 'Disable Connector', color: 'info', icon: , modal: true, modalUrl: `/api/EditExConnector?State=Disable&TenantFilter=${tenant.defaultDomainName}&GUID=${row.Guid}&Type=${row.cippconnectortype}`, - modalMessage: 'Are you sure you want to disable this rule?', + modalMessage: 'Are you sure you want to disable this connector?', }, { - label: 'Delete Rule', + label: 'Delete Connector', color: 'danger', modal: true, icon: , modalUrl: `/api/RemoveExConnector?TenantFilter=${tenant.defaultDomainName}&GUID=${row.Guid}&Type=${row.cippconnectortype}`, - modalMessage: 'Are you sure you want to delete this rule?', + modalMessage: 'Are you sure you want to delete this connector?', }, ]} placement="end" From 2894f938faff881d468349131df01a57114d0254 Mon Sep 17 00:00:00 2001 From: Esco Date: Sat, 17 Aug 2024 12:03:10 +0200 Subject: [PATCH 496/536] Improvements to MEMListPolicies --- .../endpoint/intune/MEMListCompliance.jsx | 1 + src/views/endpoint/intune/MEMListPolicies.jsx | 32 +++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/src/views/endpoint/intune/MEMListCompliance.jsx b/src/views/endpoint/intune/MEMListCompliance.jsx index 328a387566fd..87c2fdfabdf3 100644 --- a/src/views/endpoint/intune/MEMListCompliance.jsx +++ b/src/views/endpoint/intune/MEMListCompliance.jsx @@ -138,6 +138,7 @@ const ComplianceList = () => { Endpoint: 'deviceManagement/deviceCompliancePolicies', $orderby: 'displayName', $count: true, + $expand: 'assignments', }, columns, reportName: `${tenant?.defaultDomainName}-MEMPolicies-List`, diff --git a/src/views/endpoint/intune/MEMListPolicies.jsx b/src/views/endpoint/intune/MEMListPolicies.jsx index f63db28effef..725c4cc0cd42 100644 --- a/src/views/endpoint/intune/MEMListPolicies.jsx +++ b/src/views/endpoint/intune/MEMListPolicies.jsx @@ -15,6 +15,7 @@ import { CippPageList } from 'src/components/layout' import { Link } from 'react-router-dom' import { CippActionsOffcanvas, CippCodeBlock } from 'src/components/utilities' import { TitleButton } from 'src/components/buttons' +import { cellBooleanFormatter, cellDateFormatter } from 'src/components/tables' const Actions = (row, rowIndex, formatExtraData) => { const [ocVisible, setOCVisible] = useState(false) @@ -90,12 +91,43 @@ const columns = [ name: 'Name', sortable: true, exportSelector: 'displayName', + maxWidth: 'auto', }, { selector: (row) => row['PolicyTypeName'], name: 'Profile Type', sortable: true, exportSelector: 'PolicyTypeName', + maxWidth: '300px', + }, + { + selector: (row) => row['PolicyAssignment'], + name: 'Assigned', + sortable: true, + exportSelector: 'PolicyAssignment', + maxWidth: '300px', + }, + { + selector: (row) => row['PolicyExclude'], + name: 'Excluded', + sortable: true, + exportSelector: 'PolicyExclude', + maxWidth: '300px', + }, + { + selector: (row) => row['description'], + name: 'Description', + sortable: true, + exportSelector: 'description', + maxWidth: 'auto', + }, + { + selector: (row) => row['lastModifiedDateTime'], + name: 'Last Modified', + sortable: true, + exportSelector: 'lastModifiedDateTime', + cell: cellDateFormatter({ format: 'relative' }), + maxWidth: '150px', }, { selector: (row) => row['id'], From a1ce07dd3165310b15a854fb7099672fcbedabb5 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Wed, 21 Aug 2024 16:09:27 -0400 Subject: [PATCH 497/536] Add owner property to anonymization check --- src/components/tables/CippDatatable.jsx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/components/tables/CippDatatable.jsx b/src/components/tables/CippDatatable.jsx index 125eeda3e00a..7d00d9730e82 100644 --- a/src/components/tables/CippDatatable.jsx +++ b/src/components/tables/CippDatatable.jsx @@ -14,16 +14,18 @@ export default function CippDatatable({ path, params, ...rest }) { refetch, } = useListDatatableQuery({ path, params: { $filter: graphFilter, ...params } }) - let anonimized = false // Assuming default value is false + let anonymized = false // Assuming default value is false const regex = new RegExp('^[A-Z0-9]+$') const principalNameOrUPN = data[0]?.userPrincipalName ?? data[0]?.UPN ?? + data[0]?.Owner ?? data.Results?.[0]?.upn ?? - data.Results?.[0]?.userPrincipalName + data.Results?.[0]?.userPrincipalName ?? + data.Results?.[0]?.Owner if (principalNameOrUPN && regex.test(principalNameOrUPN)) { - anonimized = true + anonymized = true } var defaultFilterText = '' @@ -32,7 +34,7 @@ export default function CippDatatable({ path, params, ...rest }) { } return ( <> - {anonimized && ( + {anonymized && ( This table might contain anonymized data. Please check this Date: Wed, 21 Aug 2024 16:09:52 -0400 Subject: [PATCH 498/536] Extend permission builder for app approval --- .../utilities/CippAppPermissionBuilder.jsx | 138 ++++++++++++------ 1 file changed, 92 insertions(+), 46 deletions(-) diff --git a/src/components/utilities/CippAppPermissionBuilder.jsx b/src/components/utilities/CippAppPermissionBuilder.jsx index c986b67145f2..69d6c8c516d3 100644 --- a/src/components/utilities/CippAppPermissionBuilder.jsx +++ b/src/components/utilities/CippAppPermissionBuilder.jsx @@ -34,7 +34,14 @@ import { Editor } from '@monaco-editor/react' import { useSelector } from 'react-redux' import { CippCallout } from '../layout' -const CippAppPermissionBuilder = ({ onSubmit, currentPermissions = {}, isSubmitting }) => { +const CippAppPermissionBuilder = ({ + onSubmit, + currentPermissions = {}, + isSubmitting, + colSize = 8, + removePermissionConfirm = true, + appDisplayName = 'CIPP-SAM', +}) => { const [selectedApp, setSelectedApp] = useState([]) const [permissionsImported, setPermissionsImported] = useState(false) const [newPermissions, setNewPermissions] = useState({}) @@ -42,6 +49,7 @@ const CippAppPermissionBuilder = ({ onSubmit, currentPermissions = {}, isSubmitt const [manifestVisible, setManifestVisible] = useState(false) const currentTheme = useSelector((state) => state.app.currentTheme) const [calloutMessage, setCalloutMessage] = useState(null) + const [initialPermissions, setInitialPermissions] = useState() const { data: servicePrincipals = [], @@ -59,35 +67,49 @@ const CippAppPermissionBuilder = ({ onSubmit, currentPermissions = {}, isSubmitt var servicePrincipal = selectedApp.find((sp) => sp?.appId === appId) var newServicePrincipals = selectedApp.filter((sp) => sp?.appId !== appId) - ModalService.confirm({ - title: 'Remove Service Principal', - body: `Are you sure you want to remove ${servicePrincipal.displayName}?`, - onConfirm: () => { - setSelectedApp(newServicePrincipals) - var updatedPermissions = JSON.parse(JSON.stringify(newPermissions)) - delete updatedPermissions.Permissions[appId] - setNewPermissions(updatedPermissions) - }, - }) + if (removePermissionConfirm) { + ModalService.confirm({ + title: 'Remove Service Principal', + body: `Are you sure you want to remove ${servicePrincipal.displayName}?`, + onConfirm: () => { + setSelectedApp(newServicePrincipals) + var updatedPermissions = JSON.parse(JSON.stringify(newPermissions)) + delete updatedPermissions.Permissions[appId] + setNewPermissions(updatedPermissions) + }, + }) + } else { + setSelectedApp(newServicePrincipals) + var updatedPermissions = JSON.parse(JSON.stringify(newPermissions)) + delete updatedPermissions.Permissions[appId] + setNewPermissions(updatedPermissions) + } } const confirmReset = () => { - ModalService.confirm({ - title: 'Reset to Default', - body: 'Are you sure you want to reset all permissions to default?', - onConfirm: () => { - setSelectedApp([]) - setPermissionsImported(false) - setManifestVisible(false) - setCalloutMessage('Permissions reset to default.') - }, - }) + if (removePermissionConfirm) { + ModalService.confirm({ + title: 'Reset to Default', + body: 'Are you sure you want to reset all permissions to default?', + onConfirm: () => { + setSelectedApp([]) + setPermissionsImported(false) + setManifestVisible(false) + setCalloutMessage('Permissions reset to default.') + }, + }) + } else { + setSelectedApp([]) + setPermissionsImported(false) + setManifestVisible(false) + setCalloutMessage('Permissions reset to default.') + } } const handleSubmit = (values) => { if (onSubmit) { var postBody = { - Permissions: newPermissions, + Permissions: newPermissions.Permissions, } onSubmit(postBody) } @@ -127,30 +149,43 @@ const CippAppPermissionBuilder = ({ onSubmit, currentPermissions = {}, isSubmitt } const removePermissionRow = (servicePrincipal, permissionType, permissionId, permissionValue) => { - // modal confirm - ModalService.confirm({ - title: 'Remove Permission', - body: `Are you sure you want to remove the permission: ${permissionValue}?`, - onConfirm: () => { - var updatedPermissions = JSON.parse(JSON.stringify(newPermissions)) - var currentPermission = updatedPermissions?.Permissions[servicePrincipal][permissionType] - var newPermission = [] - if (currentPermission) { - currentPermission.map((perm) => { - if (perm.id !== permissionId) { - newPermission.push(perm) - } - }) - } - updatedPermissions.Permissions[servicePrincipal][permissionType] = newPermission - setNewPermissions(updatedPermissions) - }, - }) + if (removePermissionConfirm) { + ModalService.confirm({ + title: 'Remove Permission', + body: `Are you sure you want to remove the permission: ${permissionValue}?`, + onConfirm: () => { + var updatedPermissions = JSON.parse(JSON.stringify(newPermissions)) + var currentPermission = updatedPermissions?.Permissions[servicePrincipal][permissionType] + var newPermission = [] + if (currentPermission) { + currentPermission.map((perm) => { + if (perm.id !== permissionId) { + newPermission.push(perm) + } + }) + } + updatedPermissions.Permissions[servicePrincipal][permissionType] = newPermission + setNewPermissions(updatedPermissions) + }, + }) + } else { + var updatedPermissions = JSON.parse(JSON.stringify(newPermissions)) + var currentPermission = updatedPermissions?.Permissions[servicePrincipal][permissionType] + var newPermission = [] + if (currentPermission) { + currentPermission.map((perm) => { + if (perm.id !== permissionId) { + newPermission.push(perm) + } + }) + } + updatedPermissions.Permissions[servicePrincipal][permissionType] = newPermission + setNewPermissions(updatedPermissions) + } } - const generateManifest = (appDisplayName = 'CIPP-SAM', prompt = false) => { - if (prompt) { - // modal input form for appDisplayName + const generateManifest = ({ appDisplayName = 'CIPP-SAM', prompt = false }) => { + if (prompt || appDisplayName === '') { ModalService.prompt({ title: 'Generate Manifest', body: 'Please enter the display name for the application.', @@ -337,6 +372,11 @@ const CippAppPermissionBuilder = ({ onSubmit, currentPermissions = {}, isSubmitt }, }, }) + } else if (spSuccess & (currentPermissions !== initialPermissions)) { + setSelectedApp([]) + setNewPermissions(currentPermissions) + setInitialPermissions(currentPermissions) + setPermissionsImported(false) } else if (spSuccess && initialAppIds.length > 0 && permissionsImported == false) { var newApps = [] initialAppIds?.map((appId) => { @@ -350,10 +390,12 @@ const CippAppPermissionBuilder = ({ onSubmit, currentPermissions = {}, isSubmitt }) setSelectedApp(newApps) setNewPermissions(currentPermissions) + setInitialPermissions(currentPermissions) setPermissionsImported(true) } }, [ currentPermissions, + initialPermissions, permissionsImported, spSuccess, selectedApp, @@ -361,6 +403,7 @@ const CippAppPermissionBuilder = ({ onSubmit, currentPermissions = {}, isSubmitt setSelectedApp, setPermissionsImported, setNewPermissions, + setInitialPermissions, ]) const ApiPermissionRow = ({ servicePrincipal = null }) => { @@ -679,7 +722,7 @@ const CippAppPermissionBuilder = ({ onSubmit, currentPermissions = {}, isSubmitt return ( - + {servicePrincipals?.Metadata?.Success && ( @@ -738,7 +781,7 @@ const CippAppPermissionBuilder = ({ onSubmit, currentPermissions = {}, isSubmitt { - generateManifest() + generateManifest({ appDisplayName: appDisplayName }) }} className={`circular-button`} title={'+'} @@ -957,6 +1000,9 @@ CippAppPermissionBuilder.propTypes = { onSubmit: PropTypes.func, currentPermissions: PropTypes.object, isSubmitting: PropTypes.bool, + colSize: PropTypes.number, + removePermissionConfirm: PropTypes.bool, + appDisplayName: PropTypes.string, } export default CippAppPermissionBuilder From be79c62682621c1c145eb726c3cc5d262c8a8587 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Wed, 21 Aug 2024 16:10:08 -0400 Subject: [PATCH 499/536] approval template page --- src/views/cipp/AppApprovalTemplates.jsx | 140 ++++++++++++++++++++++++ 1 file changed, 140 insertions(+) create mode 100644 src/views/cipp/AppApprovalTemplates.jsx diff --git a/src/views/cipp/AppApprovalTemplates.jsx b/src/views/cipp/AppApprovalTemplates.jsx new file mode 100644 index 000000000000..6f9159565dcd --- /dev/null +++ b/src/views/cipp/AppApprovalTemplates.jsx @@ -0,0 +1,140 @@ +import React from 'react' +import { + CCol, + CRow, + CCallout, + CSpinner, + CButton, + CFormInput, + CFormLabel, + CTooltip, +} from '@coreui/react' +import { Field, Form, FormSpy } from 'react-final-form' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons' +import { CippPageList, CippWizard } from 'src/components/layout' +import { cellDateFormatter, CippTable, WizardTableField } from 'src/components/tables' +import PropTypes from 'prop-types' +import { + Condition, + RFFCFormCheck, + RFFCFormInput, + RFFCFormSwitch, + RFFSelectSearch, +} from 'src/components/forms' +import { useLazyGenericPostRequestQuery } from 'src/store/api/app' +import CippButtonCard from 'src/components/contentcards/CippButtonCard' +import { cellGenericFormatter } from 'src/components/tables/CellGenericFormat' +import { CippOffcanvas } from 'src/components/utilities' +import CippAppPermissionBuilder from 'src/components/utilities/CippAppPermissionBuilder' + +const AppApprovalTemplates = () => { + const [editorVisible, setEditorVisible] = React.useState(false) + const [selectedTemplate, setSelectedTemplate] = React.useState(null) + const templateNameRef = React.useRef(null) + const [genericPostRequest, postResults] = useLazyGenericPostRequestQuery() + + const onSubmit = (values) => { + var body = { + TemplateName: templateNameRef.current.value, + Permissions: values.Permissions, + } + if (selectedTemplate?.TemplateId) { + body.TemplateId = selectedTemplate.TemplateId + } + + console.log(body) + genericPostRequest({ + path: '/api/ExecAppPermissionTemplate?Action=Save', + values: body, + }).then(() => {}) + } + const titleButton = ( + { + setSelectedTemplate({}) + templateNameRef.current.value = '' + setEditorVisible(true) + }} + > + Add Template + + ) + return ( + <> + row['TemplateName'], + sortable: true, + exportSelector: 'TemplateName', + }, + { + name: 'Updated By', + selector: (row) => row['UpdatedBy'], + sortable: true, + exportSelector: 'UpdatedBy', + }, + { + name: 'Updated At', + selector: (row) => row['Timestamp'], + sortable: true, + exportSelector: 'Timestamp', + cell: cellDateFormatter({ format: 'short' }), + }, + { + name: 'Actions', + cell: (row) => ( + + { + setSelectedTemplate(row) + templateNameRef.current.value = row.TemplateName + setEditorVisible(true) + }} + > + + + + ), + }, + ], + reportName: 'AppApprovalTemplates', + }} + /> + setEditorVisible(false)} + > + Template Name + + Permissions + + + + ) +} + +export default AppApprovalTemplates From 8ecaf078015c56cfd83c5ef08a0c97ca81f15cf5 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Wed, 21 Aug 2024 16:41:20 -0400 Subject: [PATCH 500/536] Hide permission builder while loading --- .../components/SettingsAppPermissions.jsx | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/views/cipp/app-settings/components/SettingsAppPermissions.jsx b/src/views/cipp/app-settings/components/SettingsAppPermissions.jsx index d36dffb0b597..55a56aea7f5e 100644 --- a/src/views/cipp/app-settings/components/SettingsAppPermissions.jsx +++ b/src/views/cipp/app-settings/components/SettingsAppPermissions.jsx @@ -21,6 +21,7 @@ import { useListTenantsQuery } from 'src/store/api/tenants' import { OffcanvasListSection } from 'src/components/utilities/CippListOffcanvas' import CippButtonCard from 'src/components/contentcards/CippButtonCard' import CippAppPermissionBuilder from 'src/components/utilities/CippAppPermissionBuilder' +import Skeleton from 'react-loading-skeleton' const SettingsAppPermissions = () => { const [genericPostRequest, postResults] = useLazyGenericPostRequestQuery() @@ -36,6 +37,7 @@ const SettingsAppPermissions = () => { const { data: samAppPermissions = [], isFetching: samAppPermissionsFetching, + isSuccess: samAppPermissionsSuccess, refetch: refetchSam, } = useGenericGetRequestQuery({ path: 'api/ExecSAMAppPermissions', @@ -51,11 +53,14 @@ const SettingsAppPermissions = () => { advised.

    - + {samAppPermissionsFetching && } + {samAppPermissionsSuccess && ( + + )} {postResults.data && ( From 8e16606ee435f2c84e5f81a70d18a141ed36d8d0 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Wed, 21 Aug 2024 18:37:02 -0400 Subject: [PATCH 501/536] Add username to BEC refresh --- src/views/identity/administration/ViewBEC.jsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/views/identity/administration/ViewBEC.jsx b/src/views/identity/administration/ViewBEC.jsx index 4f1173042008..3142af80d61e 100644 --- a/src/views/identity/administration/ViewBEC.jsx +++ b/src/views/identity/administration/ViewBEC.jsx @@ -239,6 +239,7 @@ const ViewBec = () => { execBecView({ tenantFilter: tenantDomain, userId: userId, + userName: userName, overwrite: true, }) } From 3bb85f83a000a74f4be95ffb37c956ef6d2844d5 Mon Sep 17 00:00:00 2001 From: cipptesting Date: Wed, 21 Aug 2024 18:47:49 -0400 Subject: [PATCH 502/536] Added Anti-Phishing and Anti-Spam policies to backup --- src/views/tenant/backup/CreateBackup.jsx | 9 +++++++++ src/views/tenant/backup/RestoreBackup.jsx | 7 +++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/views/tenant/backup/CreateBackup.jsx b/src/views/tenant/backup/CreateBackup.jsx index faec25e074a3..d0dd6350219c 100644 --- a/src/views/tenant/backup/CreateBackup.jsx +++ b/src/views/tenant/backup/CreateBackup.jsx @@ -194,6 +194,15 @@ const CreateBackup = () => { name="intuneprotection" label="Intune Protection Policies" /> +

    Email Security

    + +

    CIPP

    {

    Conditional Access

    -
    -

    Intune

    +
    + +

    Email Security

    + +

    CIPP

    From f690aff93493ea553b87d5d30316d009977196f3 Mon Sep 17 00:00:00 2001 From: OwenC3 <156828136+OwenC3@users.noreply.github.com> Date: Thu, 22 Aug 2024 16:56:07 -0400 Subject: [PATCH 503/536] Update standards.json --- src/data/standards.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/data/standards.json b/src/data/standards.json index 45e7c0438ea1..4b12a7181b10 100644 --- a/src/data/standards.json +++ b/src/data/standards.json @@ -349,7 +349,7 @@ "name": "standards.TAP", "cat": "Entra (AAD) Standards", "tag": ["lowimpact"], - "helpText": "Enables TAP and sets the default TAP lifetime to 1 hour. This configuration also allows you to select is a TAP is single use or multi-logon.", + "helpText": "Enables TAP and sets the default TAP lifetime to 1 hour. This configuration also allows you to select if a TAP is single use or multi-logon.", "docsDescription": "Enables Temporary Password generation for the tenant.", "addedComponent": [ { @@ -648,7 +648,7 @@ "name": "standards.DisableEmail", "cat": "Entra (AAD) Standards", "tag": ["highimpact"], - "helpText": "This blocks users from using email as an MFA method. This disables the email OTP option for guest users, and instead promts them to create a Microsoft account.", + "helpText": "This blocks users from using email as an MFA method. This disables the email OTP option for guest users, and instead prompts them to create a Microsoft account.", "addedComponent": [], "label": "Disables Email as an MFA method", "impact": "High Impact", From 2fd93963586bd387d32752c453ee206966d56b40 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 22 Aug 2024 17:42:52 -0400 Subject: [PATCH 504/536] Update CippAppPermissionBuilder.jsx --- src/components/utilities/CippAppPermissionBuilder.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/utilities/CippAppPermissionBuilder.jsx b/src/components/utilities/CippAppPermissionBuilder.jsx index 69d6c8c516d3..d270cfc4a7a7 100644 --- a/src/components/utilities/CippAppPermissionBuilder.jsx +++ b/src/components/utilities/CippAppPermissionBuilder.jsx @@ -807,7 +807,7 @@ const CippAppPermissionBuilder = ({ title="Import Manifest" id="importManifest" visible={manifestVisible} - onHide={() => { + hideFunction={() => { setManifestVisible(false) }} addedClass="offcanvas-large" From 4fb3eae37824fc55945eeac306ad492253a4e845 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Kj=C3=A6rg=C3=A5rd?= Date: Sat, 24 Aug 2024 15:44:51 +0200 Subject: [PATCH 505/536] Add delete rule button to mailboxrulelist --- .../administration/MailboxRuleList.jsx | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/src/views/email-exchange/administration/MailboxRuleList.jsx b/src/views/email-exchange/administration/MailboxRuleList.jsx index 34f3798900d8..53d4d57cbc6e 100644 --- a/src/views/email-exchange/administration/MailboxRuleList.jsx +++ b/src/views/email-exchange/administration/MailboxRuleList.jsx @@ -3,6 +3,43 @@ import { useSelector } from 'react-redux' import { CippPageList } from 'src/components/layout' import { CellTip } from 'src/components/tables' import { cellGenericFormatter } from 'src/components/tables/CellGenericFormat' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { CButton } from '@coreui/react' +import { faTrash } from '@fortawesome/free-solid-svg-icons' +import { ModalService } from 'src/components/utilities' +import { useLazyGenericGetRequestQuery } from 'src/store/api/app' + +const DeleteMailboxRuleButton = (ruleId, userPrincipalName, ruleName) => { + const tenant = useSelector((state) => state.app.currentTenant) + const [genericGetRequest, getResults] = useLazyGenericGetRequestQuery() + const handleModal = (modalMessage, modalUrl) => { + ModalService.confirm({ + body: ( +
    +
    {modalMessage}
    +
    + ), + title: 'Confirm', + onConfirm: () => genericGetRequest({ path: modalUrl }), + }) + } + return ( + { + ModalService.confirm( + handleModal( + 'Are you sure you want to remove this mailbox rule?', + `/api/ExecRemoveMailboxRule?TenantFilter=${tenant?.defaultDomainName}&ruleId=${ruleId}&ruleName=${ruleName}&userPrincipalName=${userPrincipalName}`, + ), + ) + }} + > + + + ) +} const MailboxRuleList = () => { const tenant = useSelector((state) => state.app.currentTenant) @@ -62,6 +99,12 @@ const MailboxRuleList = () => { exportSelector: 'ForwardTo', cell: cellGenericFormatter(), }, + { + name: 'Action', + maxWidth: '100px', + cell: (row) => + DeleteMailboxRuleButton(row['Identity'], row['UserPrincipalName'], row['Name']), + }, ] return ( From 1f71fe93797cdce9172173347a44f1709e3f03f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Kj=C3=A6rg=C3=A5rd?= Date: Sat, 24 Aug 2024 16:14:33 +0200 Subject: [PATCH 506/536] Add todo's --- src/views/email-exchange/administration/MailboxRuleList.jsx | 2 ++ src/views/identity/administration/ViewUser.jsx | 3 +++ 2 files changed, 5 insertions(+) diff --git a/src/views/email-exchange/administration/MailboxRuleList.jsx b/src/views/email-exchange/administration/MailboxRuleList.jsx index 53d4d57cbc6e..b941e501e01b 100644 --- a/src/views/email-exchange/administration/MailboxRuleList.jsx +++ b/src/views/email-exchange/administration/MailboxRuleList.jsx @@ -108,6 +108,8 @@ const MailboxRuleList = () => { ] return ( + // TODO: Add support for displaying the result of the delete operation. Currently, the delete operation is performed but the result is not displayed anywhere but the networking tab of the dev tools in the browser. + // All API code is in place and should return the needed HTTP status information. -Bobby { + {/* // TODO: Add support for displaying the result of the delete operation. Currently, the delete operation is performed but the result is not displayed anywhere but the networking tab of the dev tools in the browser. + All API code is in place and should return the needed HTTP status information. + Possibly even remove the row in the table if the delete operation was successful? -Bobby */} Date: Sat, 24 Aug 2024 16:15:01 +0200 Subject: [PATCH 507/536] feat: Add delete rule button to mailboxrulelist --- .../administration/UserMailboxRuleList.jsx | 43 ++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/src/views/identity/administration/UserMailboxRuleList.jsx b/src/views/identity/administration/UserMailboxRuleList.jsx index ae2592866e85..f0a1e05a4d31 100644 --- a/src/views/identity/administration/UserMailboxRuleList.jsx +++ b/src/views/identity/administration/UserMailboxRuleList.jsx @@ -2,7 +2,12 @@ import React from 'react' import PropTypes from 'prop-types' import { CellBoolean, cellBooleanFormatter, CellTip } from 'src/components/tables' import { DatatableContentCard } from 'src/components/contentcards' -import { faEnvelope } from '@fortawesome/free-solid-svg-icons' +import { faEnvelope, faTrash } from '@fortawesome/free-solid-svg-icons' +import { cellGenericFormatter } from 'src/components/tables/CellGenericFormat' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { CButton } from '@coreui/react' +import { ModalService } from 'src/components/utilities' +import { useLazyGenericGetRequestQuery } from 'src/store/api/app' const rowStyle = (row, rowIndex) => { const style = {} @@ -10,6 +15,37 @@ const rowStyle = (row, rowIndex) => { return style } +const DeleteMailboxRuleButton = (tenantDomain, ruleId, userPrincipalName, ruleName) => { + const [genericGetRequest, getResults] = useLazyGenericGetRequestQuery() + const handleModal = (modalMessage, modalUrl) => { + ModalService.confirm({ + body: ( +
    +
    {modalMessage}
    +
    + ), + title: 'Confirm', + onConfirm: () => genericGetRequest({ path: modalUrl }), + }) + } + return ( + { + ModalService.confirm( + handleModal( + 'Are you sure you want to remove this mailbox rule?', + `/api/ExecRemoveMailboxRule?TenantFilter=${tenantDomain}&ruleId=${ruleId}&ruleName=${ruleName}&userPrincipalName=${userPrincipalName}`, + ), + ) + }} + > + + + ) +} + export default function UserMailboxRuleList({ userId, userEmail, tenantDomain, className = null }) { const formatter = (cell) => CellBoolean({ cell }) const columns = [ @@ -70,6 +106,11 @@ export default function UserMailboxRuleList({ userId, userEmail, tenantDomain, c exportSelector: 'DeleteMessage', width: '200px', }, + { + name: 'Action', + maxWidth: '100px', + cell: (row) => DeleteMailboxRuleButton(tenantDomain, row['Identity'], userEmail, row['Name']), + }, ] return ( Date: Sat, 24 Aug 2024 17:16:08 +0200 Subject: [PATCH 508/536] Fix angry linter --- src/views/identity/administration/DeployJITAdmin.jsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/views/identity/administration/DeployJITAdmin.jsx b/src/views/identity/administration/DeployJITAdmin.jsx index 146ab7d6fef1..b8d5945f001b 100644 --- a/src/views/identity/administration/DeployJITAdmin.jsx +++ b/src/views/identity/administration/DeployJITAdmin.jsx @@ -23,7 +23,6 @@ import 'react-datepicker/dist/react-datepicker.css' 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 GDAPRoles from 'src/data/GDAPRoles' import { CippDatatable, cellDateFormatter } from 'src/components/tables' From b403887bb0689b6f1edff21d44f0fc37f94db50a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Kj=C3=A6rg=C3=A5rd?= Date: Sat, 24 Aug 2024 17:17:45 +0200 Subject: [PATCH 509/536] Fix another angry linter --- src/views/tenant/backup/CreateBackup.jsx | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/views/tenant/backup/CreateBackup.jsx b/src/views/tenant/backup/CreateBackup.jsx index d0dd6350219c..c4dbf63ba81c 100644 --- a/src/views/tenant/backup/CreateBackup.jsx +++ b/src/views/tenant/backup/CreateBackup.jsx @@ -195,14 +195,8 @@ const CreateBackup = () => { label="Intune Protection Policies" />

    Email Security

    - - + +

    CIPP

    Date: Mon, 26 Aug 2024 21:24:34 -0400 Subject: [PATCH 510/536] Quarantine messages --- src/scss/_custom.scss | 4 ++ .../administration/QuarantineList.jsx | 52 ++++++++++++++++--- .../email-exchange/tools/MessageViewer.jsx | 2 +- 3 files changed, 51 insertions(+), 7 deletions(-) diff --git a/src/scss/_custom.scss b/src/scss/_custom.scss index b118641b872e..2b1e6c2aa248 100644 --- a/src/scss/_custom.scss +++ b/src/scss/_custom.scss @@ -257,6 +257,10 @@ h3.underline:after { } } +.modal { + z-index: 1555 !important; +} + .modal-content { border-radius: var(--cipp-border-radius); } diff --git a/src/views/email-exchange/administration/QuarantineList.jsx b/src/views/email-exchange/administration/QuarantineList.jsx index 6c5de828afec..cc3ac793ad53 100644 --- a/src/views/email-exchange/administration/QuarantineList.jsx +++ b/src/views/email-exchange/administration/QuarantineList.jsx @@ -1,22 +1,44 @@ import React, { useState } from 'react' import { useSelector } from 'react-redux' import { CippPageList } from 'src/components/layout' -import { CButton } from '@coreui/react' +import { CButton, CSpinner, CTooltip } from '@coreui/react' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { faEllipsisV, faMinusCircle, faPaperPlane } from '@fortawesome/free-solid-svg-icons' -import { CippActionsOffcanvas } from 'src/components/utilities' +import { CippActionsOffcanvas, CippOffcanvas } from 'src/components/utilities' import { cellDateFormatter, CellTip } from 'src/components/tables' +import { MessageViewer } from 'src/views/email-exchange/tools/MessageViewer' +import { ModalService } from 'src/components/utilities' +import { useLazyGenericGetRequestQuery } from 'src/store/api/app' +import PropTypes from 'prop-types' const QuarantineList = () => { const tenant = useSelector((state) => state.app.currentTenant) - const Offcanvas = (row, rowIndex, formatExtraData) => { const [ocVisible, setOCVisible] = useState(false) + const [msgOcVisible, setMsgOcVisible] = useState(false) + const [getQuarantineMessage, quarantineMessage] = useLazyGenericGetRequestQuery() return ( <> - setOCVisible(true)}> - - + + { + setMsgOcVisible(true) + getQuarantineMessage({ + path: `/api/ListMailQuarantineMessage`, + params: { TenantFilter: tenant.defaultDomainName, Identity: row?.Identity }, + }) + }} + > + + + + + setOCVisible(true)}> + + + { id={row.id} hideFunction={() => setOCVisible(false)} /> + setMsgOcVisible(false)} + visible={msgOcVisible} + placement="end" + > + <> + {quarantineMessage.isLoading && ( +
    + Loading message +
    + )} + {quarantineMessage.isSuccess && ( + + )} + +
    ) } diff --git a/src/views/email-exchange/tools/MessageViewer.jsx b/src/views/email-exchange/tools/MessageViewer.jsx index f010c0b37c4f..bc63e647b186 100644 --- a/src/views/email-exchange/tools/MessageViewer.jsx +++ b/src/views/email-exchange/tools/MessageViewer.jsx @@ -23,7 +23,7 @@ import DOMPurify from 'dompurify' import ReactHtmlParser from 'react-html-parser' import CippDropzone from 'src/components/utilities/CippDropzone' -const MessageViewer = ({ emailSource }) => { +export const MessageViewer = ({ emailSource }) => { const [emlContent, setEmlContent] = useState(null) const [emlError, setEmlError] = useState(false) const [messageHtml, setMessageHtml] = useState('') From c47696393f16ea647f3d7ef4d276a28ea4b74426 Mon Sep 17 00:00:00 2001 From: RunningFreak <156828136+OwenIbarra@users.noreply.github.com> Date: Tue, 27 Aug 2024 12:38:28 -0400 Subject: [PATCH 511/536] Update AzureDeploymentTemplate.json --- deployment/AzureDeploymentTemplate.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deployment/AzureDeploymentTemplate.json b/deployment/AzureDeploymentTemplate.json index ff7e9a97bada..dc3222498ec6 100644 --- a/deployment/AzureDeploymentTemplate.json +++ b/deployment/AzureDeploymentTemplate.json @@ -200,7 +200,7 @@ "name": "[variables('funcStorageName')]", "location": "[resourceGroup().location]", "tags": { - "displayName": "funStorageName" + "displayName": "funcStorageName" }, "sku": { "name": "Standard_LRS" From 2173e51d8e6586755842c6ad785c474eb1a3bed8 Mon Sep 17 00:00:00 2001 From: RunningFreak <156828136+OwenIbarra@users.noreply.github.com> Date: Tue, 27 Aug 2024 12:41:33 -0400 Subject: [PATCH 512/536] Update AzureDeploymentTemplate_regionoptions.json --- deployment/AzureDeploymentTemplate_regionoptions.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/deployment/AzureDeploymentTemplate_regionoptions.json b/deployment/AzureDeploymentTemplate_regionoptions.json index bc2f24089d4d..ac6691593a0b 100644 --- a/deployment/AzureDeploymentTemplate_regionoptions.json +++ b/deployment/AzureDeploymentTemplate_regionoptions.json @@ -6,7 +6,7 @@ "defaultValue": "CIPP", "type": "string", "metadata": { - "description": "Name use as base-template to named the resources deployed in Azure." + "description": "Name used as base-template to name the resources deployed in Azure." } }, "GithubRepository": { @@ -20,7 +20,7 @@ "defaultValue": "GeneratedPassword", "type": "string", "metadata": { - "description": "Your Github Repository token (see https://docs.microsoft.com/en-us/azure/static-web-apps/publish-azure-resource-manager?tabs=azure-cli#create-a-github-personal-access-token" + "description": "Your Github Repository token (see https://docs.microsoft.com/en-us/azure/static-web-apps/publish-azure-resource-manager?tabs=azure-cli#create-a-github-personal-access-token)" } }, "GithubAPIRepository": { @@ -197,7 +197,7 @@ "name": "[variables('funcStorageName')]", "location": "[resourceGroup().location]", "tags": { - "displayName": "funStorageName" + "displayName": "funcStorageName" }, "sku": { "name": "Standard_LRS" From b72a4e7df56941dea5aad475790046575829355c Mon Sep 17 00:00:00 2001 From: RunningFreak <156828136+OwenIbarra@users.noreply.github.com> Date: Tue, 27 Aug 2024 12:43:20 -0400 Subject: [PATCH 513/536] Update DevAzureDeploymentTemplate.json --- deployment/DevAzureDeploymentTemplate.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/deployment/DevAzureDeploymentTemplate.json b/deployment/DevAzureDeploymentTemplate.json index 8beefadd450c..5e6077beab41 100644 --- a/deployment/DevAzureDeploymentTemplate.json +++ b/deployment/DevAzureDeploymentTemplate.json @@ -6,7 +6,7 @@ "defaultValue": "CIPPDev", "type": "string", "metadata": { - "description": "Name use as base-template to named the resources deployed in Azure." + "description": "Name used as base-template to name the resources deployed in Azure." } }, "TenantID": { @@ -48,7 +48,7 @@ "defaultValue": "GeneratedPassword", "type": "string", "metadata": { - "description": "Your Github Repository token (see https://docs.microsoft.com/en-us/azure/static-web-apps/publish-azure-resource-manager?tabs=azure-cli#create-a-github-personal-access-token" + "description": "Your Github Repository token (see https://docs.microsoft.com/en-us/azure/static-web-apps/publish-azure-resource-manager?tabs=azure-cli#create-a-github-personal-access-token)" } }, "GithubAPIRepository": { @@ -250,7 +250,7 @@ "name": "[variables('funcStorageName')]", "location": "[resourceGroup().location]", "tags": { - "displayName": "funStorageName" + "displayName": "funcStorageName" }, "sku": { "name": "Standard_LRS" From 1c4d5dd313e6e51dc11a801cc7ee48aea1cd91a5 Mon Sep 17 00:00:00 2001 From: RunningFreak <156828136+OwenIbarra@users.noreply.github.com> Date: Tue, 27 Aug 2024 12:44:08 -0400 Subject: [PATCH 514/536] Update DevAzureDeploymentTemplate_regionoptions.json --- deployment/DevAzureDeploymentTemplate_regionoptions.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/deployment/DevAzureDeploymentTemplate_regionoptions.json b/deployment/DevAzureDeploymentTemplate_regionoptions.json index 0fb300af06c9..f41e669076e5 100644 --- a/deployment/DevAzureDeploymentTemplate_regionoptions.json +++ b/deployment/DevAzureDeploymentTemplate_regionoptions.json @@ -6,7 +6,7 @@ "defaultValue": "CIPP", "type": "string", "metadata": { - "description": "Name use as base-template to named the resources deployed in Azure." + "description": "Name used as base-template to name the resources deployed in Azure." } }, "TenantID": { @@ -48,7 +48,7 @@ "defaultValue": "GeneratedPassword", "type": "string", "metadata": { - "description": "Your Github Repository token (see https://docs.microsoft.com/en-us/azure/static-web-apps/publish-azure-resource-manager?tabs=azure-cli#create-a-github-personal-access-token" + "description": "Your Github Repository token (see https://docs.microsoft.com/en-us/azure/static-web-apps/publish-azure-resource-manager?tabs=azure-cli#create-a-github-personal-access-token)" } }, "GithubAPIRepository": { From 608343461800e7f36d59a7223a5e0fb02f08722b Mon Sep 17 00:00:00 2001 From: RunningFreak <156828136+OwenIbarra@users.noreply.github.com> Date: Tue, 27 Aug 2024 12:56:58 -0400 Subject: [PATCH 515/536] Update SettingsExtensionMappings.jsx --- src/views/cipp/app-settings/SettingsExtensionMappings.jsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/views/cipp/app-settings/SettingsExtensionMappings.jsx b/src/views/cipp/app-settings/SettingsExtensionMappings.jsx index 6d7a63d2a3d0..4ed3362e2b59 100644 --- a/src/views/cipp/app-settings/SettingsExtensionMappings.jsx +++ b/src/views/cipp/app-settings/SettingsExtensionMappings.jsx @@ -169,7 +169,7 @@ export function SettingsExtensionMappings({ type }) { ) } - const halocolumns = [ + const haloColumns = [ { name: 'Tenant', selector: (row) => row.Tenant?.displayName, @@ -205,7 +205,7 @@ export function SettingsExtensionMappings({ type }) { }, ] - const ninjacolumns = [ + const ninjaColumns = [ { name: 'Tenant', selector: (row) => row.Tenant?.displayName, @@ -286,7 +286,7 @@ export function SettingsExtensionMappings({ type }) { @@ -439,7 +439,7 @@ export function SettingsExtensionMappings({ type }) { From a9b88a27b81c1c20bddaba700e444502eee0e96c Mon Sep 17 00:00:00 2001 From: John Duprey Date: Tue, 27 Aug 2024 21:18:24 -0400 Subject: [PATCH 516/536] Update QuarantineList.jsx --- src/views/email-exchange/administration/QuarantineList.jsx | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/views/email-exchange/administration/QuarantineList.jsx b/src/views/email-exchange/administration/QuarantineList.jsx index cc3ac793ad53..87582178f38d 100644 --- a/src/views/email-exchange/administration/QuarantineList.jsx +++ b/src/views/email-exchange/administration/QuarantineList.jsx @@ -10,6 +10,7 @@ import { MessageViewer } from 'src/views/email-exchange/tools/MessageViewer' import { ModalService } from 'src/components/utilities' import { useLazyGenericGetRequestQuery } from 'src/store/api/app' import PropTypes from 'prop-types' +import Skeleton from 'react-loading-skeleton' const QuarantineList = () => { const tenant = useSelector((state) => state.app.currentTenant) @@ -95,11 +96,7 @@ const QuarantineList = () => { placement="end" > <> - {quarantineMessage.isLoading && ( -
    - Loading message -
    - )} + {quarantineMessage.isLoading && } {quarantineMessage.isSuccess && ( )} From 43491fff83809857bb19b601088a6088e7f3ffb3 Mon Sep 17 00:00:00 2001 From: RunningFreak <156828136+OwenIbarra@users.noreply.github.com> Date: Wed, 28 Aug 2024 10:51:17 -0400 Subject: [PATCH 517/536] Typo --- src/views/endpoint/applications/ApplicationsAddRMM.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/endpoint/applications/ApplicationsAddRMM.jsx b/src/views/endpoint/applications/ApplicationsAddRMM.jsx index a6583e00fd4c..e90e31ff78e3 100644 --- a/src/views/endpoint/applications/ApplicationsAddRMM.jsx +++ b/src/views/endpoint/applications/ApplicationsAddRMM.jsx @@ -325,7 +325,7 @@ const AddRMM = () => {
    From 2b4f94abdb319c70d9b2f146b67365eb629c3f96 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Wed, 28 Aug 2024 16:52:33 -0400 Subject: [PATCH 518/536] BEC prettification --- src/store/api/users.js | 1 + src/views/identity/administration/ViewBEC.jsx | 76 ++++++++++++------- 2 files changed, 48 insertions(+), 29 deletions(-) diff --git a/src/store/api/users.js b/src/store/api/users.js index 7ef16f12fdb3..552481567406 100644 --- a/src/store/api/users.js +++ b/src/store/api/users.js @@ -64,6 +64,7 @@ export const usersApi = baseApi.injectEndpoints({ userId: _args.userId, tenantFilter: _args.tenantFilter, userName: _args.userName, + overwrite: _args.overwrite, }, }) if (startRequest.error) { diff --git a/src/views/identity/administration/ViewBEC.jsx b/src/views/identity/administration/ViewBEC.jsx index 3142af80d61e..9a4338d79b88 100644 --- a/src/views/identity/administration/ViewBEC.jsx +++ b/src/views/identity/administration/ViewBEC.jsx @@ -22,6 +22,7 @@ import { CippContentCard, CippMasonry, CippMasonryItem, CippPage } from 'src/com import 'react-loading-skeleton/dist/skeleton.css' import Skeleton from 'react-loading-skeleton' import useConfirmModal from 'src/hooks/useConfirmModal' +import { cellGenericFormatter } from 'src/components/tables/CellGenericFormat' const ViewBec = () => { let query = useQuery() @@ -85,28 +86,33 @@ const ViewBec = () => { const logonColumns = [ { name: 'App', - selector: (row) => row['AppDisplayName'], + selector: (row) => row['appDisplayName'], sortable: true, + cell: cellGenericFormatter(), }, { name: 'Date Time', - selector: (row) => row['CreatedDateTime'], + selector: (row) => row['createdDateTime'], sortable: true, + cell: cellGenericFormatter(), }, { name: 'Error code', selector: (row) => row.id, sortable: true, + cell: cellGenericFormatter(), }, { name: 'Details', selector: (row) => row.Status, sortable: true, + cell: cellGenericFormatter(), }, { name: 'IP', selector: (row) => row.IPAddress, sortable: true, + cell: cellGenericFormatter(), }, ] @@ -115,21 +121,25 @@ const ViewBec = () => { name: 'IP', selector: (row) => row['IPAddress'], sortable: true, + cell: cellGenericFormatter(), }, { name: 'User', selector: (row) => row['userPrincipalName'], sortable: true, + cell: cellGenericFormatter(), }, { name: 'Application', selector: (row) => row['AppDisplayName'], sortable: true, + cell: cellGenericFormatter(), }, { name: 'Result', selector: (row) => row['Status'], sortable: true, + cell: cellGenericFormatter(), }, ] const newUserColumns = [ @@ -137,16 +147,19 @@ const ViewBec = () => { name: 'DisplayName', selector: (row) => row['displayName'], sortable: true, + cell: cellGenericFormatter(), }, { name: 'Username', selector: (row) => row['userPrincipalName'], sortable: true, + cell: cellGenericFormatter(), }, { name: 'Date', - selector: (row) => row['CreatedDateTime'], + selector: (row) => row['createdDateTime'], sortable: true, + cell: cellGenericFormatter(), }, ] @@ -155,16 +168,19 @@ const ViewBec = () => { name: 'displayName', selector: (row) => row['displayName'], sortable: true, + cell: cellGenericFormatter(), }, { name: 'Username', selector: (row) => row['userPrincipalName'], sortable: true, + cell: cellGenericFormatter(), }, { name: 'Date', selector: (row) => row['lastPasswordChangeDateTime'], sortable: true, + cell: cellGenericFormatter(), }, ] @@ -173,44 +189,46 @@ const ViewBec = () => { name: 'Operation', selector: (row) => row['Operation'], sortable: true, + cell: cellGenericFormatter(), }, { name: 'Executed by', selector: (row) => row['UserKey'], sortable: true, + cell: cellGenericFormatter(), }, { name: 'Executed on', selector: (row) => row['ObjectId'], sortable: true, + cell: cellGenericFormatter(), }, { name: 'Permissions', selector: (row) => row['Permissions'], sortable: true, + cell: cellGenericFormatter(), }, ] const appColumns = [ { - name: 'Type', - selector: (row) => row['Operation'], - sortable: true, - }, - { - name: 'User', - selector: (row) => row['UserId'], + name: 'Application', + selector: (row) => row['appDisplayName'], sortable: true, + cell: cellGenericFormatter(), }, { - name: 'Application', - selector: (row) => row['ObjectId'], + name: 'Application ID', + selector: (row) => row['appId'], sortable: true, + cell: cellGenericFormatter(), }, { - name: 'Result', - selector: (row) => row['ResultStatus'], + name: 'Created', + selector: (row) => row['createdDateTime'], sortable: true, + cell: cellGenericFormatter(), }, ] const handleReMediate = useConfirmModal({ @@ -304,9 +322,9 @@ const ViewBec = () => { data={alerts.SuspectUserDevices} striped responsive={true} - tableProps={{ subHeaderComponent: false, pagination: false }} + isModal={true} wrapperClasses="table-responsive" - reportName="none" + reportName="bec-user-devices" /> )} @@ -322,8 +340,8 @@ const ViewBec = () => { data={alerts.NewRules} striped responsive={true} - tableProps={{ subHeaderComponent: false }} - reportName="none" + isModal={true} + reportName="bec-inbox-rules" /> )} @@ -338,8 +356,8 @@ const ViewBec = () => { data={alerts.LastSuspectUserLogon} striped responsive={true} - tableProps={{ subHeaderComponent: false }} - reportName="none" + isModal={true} + reportName="bec-suspect-user-logons" /> )} @@ -355,9 +373,9 @@ const ViewBec = () => { data={alerts.NewUsers} striped responsive={true} - tableProps={{ subHeaderComponent: false }} + isModal={true} wrapperClasses="table-responsive" - reportName="none" + reportName="bec-new-users" /> )} @@ -373,9 +391,9 @@ const ViewBec = () => { data={alerts.ChangedPasswords} striped responsive={true} - tableProps={{ subHeaderComponent: false }} + isModal={true} wrapperClasses="table-responsive" - reportName="none" + reportName="bec-changed-passwords" /> )} @@ -391,9 +409,9 @@ const ViewBec = () => { data={alerts.MailboxPermissionChanges} striped responsive={true} - tableProps={{ subHeaderComponent: false }} + isModal={true} wrapperClasses="table-responsive" - reportName="none" + reportName="bec-mailbox-permission-changes" /> )} @@ -409,9 +427,9 @@ const ViewBec = () => { data={alerts.AddedApps} striped responsive={true} - tableProps={{ subHeaderComponent: false }} wrapperClasses="table-responsive" - reportName="none" + isModal={true} + reportName="bec-added-apps" /> )} @@ -427,7 +445,7 @@ const ViewBec = () => { data={alerts.SuspectUserMailboxLogons} striped responsive={true} - tableProps={{ subHeaderComponent: false }} + isModal={true} wrapperClasses="table-responsive" reportName="none" /> From 8a04e24681a08bcfb5dd5a931b030b4c13c5e9d3 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 29 Aug 2024 10:25:39 -0400 Subject: [PATCH 519/536] Update orchestration starter url --- src/views/endpoint/applications/ListApplicationQueue.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/endpoint/applications/ListApplicationQueue.jsx b/src/views/endpoint/applications/ListApplicationQueue.jsx index 53f1e9197d33..eb42ed86ec47 100644 --- a/src/views/endpoint/applications/ListApplicationQueue.jsx +++ b/src/views/endpoint/applications/ListApplicationQueue.jsx @@ -20,7 +20,7 @@ const RefreshAction = () => { Please note: This job runs automatically every 12 hours.
    ), - onConfirm: () => execStandards({ path: 'api/AddChocoApp_OrchestrationStarter' }), + onConfirm: () => execStandards({ path: 'api/ExecAppUpload' }), }) return ( From fae4360d9cd5bee3db671bf5aacd95812b4e94d0 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 29 Aug 2024 10:42:36 -0400 Subject: [PATCH 520/536] Cleanup queries in Mailbox Settings - Change forwarding to look at proxyAddresses instead of mail - Remove extra queries --- .../administration/EditMailboxPermissions.jsx | 21 ++++--------------- 1 file changed, 4 insertions(+), 17 deletions(-) diff --git a/src/views/email-exchange/administration/EditMailboxPermissions.jsx b/src/views/email-exchange/administration/EditMailboxPermissions.jsx index 3f16a1e389d9..ff1e7d2b64ce 100644 --- a/src/views/email-exchange/administration/EditMailboxPermissions.jsx +++ b/src/views/email-exchange/administration/EditMailboxPermissions.jsx @@ -195,7 +195,7 @@ const MailboxPermissions = () => { params: { Endpoint: 'users', TenantFilter: tenantDomain, - $filter: 'assignedLicenses/$count ne 0 and accountEnabled eq true', + $filter: "assignedLicenses/$count ne 0 and accountEnabled eq true and userType eq 'Member'", $count: true, }, }) @@ -586,7 +586,7 @@ const MailboxForwarding = () => { params: { Endpoint: 'users', TenantFilter: tenantDomain, - $filter: "userType eq 'Member' and mail ge ' '", // filter out guests and users with no mailbox. #HACK "mail ne 'null'" does not work so this horrible hack is required + $filter: "userType eq 'Member' and proxyAddresses/$count ne 0", }, }) useEffect(() => { @@ -803,19 +803,6 @@ const OutOfOffice = () => { error: userError, } = useListMailboxPermissionsQuery({ tenantDomain, userId }) - const { - data: users = [], - isFetching: usersIsFetching, - error: usersError, - } = useGenericGetRequestQuery({ - path: '/api/ListGraphRequest', - params: { - Endpoint: 'users', - TenantFilter: tenantDomain, - $filter: 'assignedLicenses/$count ne 0 and accountEnabled eq true', - $count: true, - }, - }) useEffect(() => { if (postResults.isSuccess) { // @TODO do something here? @@ -865,9 +852,9 @@ const OutOfOffice = () => { )} - {usersIsFetching && } + {userIsFetching && } {userError && Error loading user} - {!usersIsFetching && ( + {!userIsFetching && ( Date: Thu, 29 Aug 2024 11:10:00 -0400 Subject: [PATCH 521/536] Update paths for starting BPA/Domain Analyser --- src/store/api/reports.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/store/api/reports.js b/src/store/api/reports.js index 83be1b4fa361..2292662ad2be 100644 --- a/src/store/api/reports.js +++ b/src/store/api/reports.js @@ -6,10 +6,10 @@ export const reportsApi = baseApi.injectEndpoints({ query: () => ({ path: '/api/BestPracticeAnalyser_List' }), }), execBestPracticeAnalyser: builder.mutation({ - query: () => ({ path: '/api/BestPracticeAnalyser_OrchestrationStarter' }), + query: () => ({ path: '/api/ExecBPA' }), }), execDomainsAnalyser: builder.mutation({ - query: () => ({ path: '/api/DomainAnalyser_OrchestrationStarter' }), + query: () => ({ path: '/api/ExecDomainAnalyser' }), }), }), }) From 0c308b2abb08275824b835526368f7af8a42febb Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 29 Aug 2024 11:17:01 -0400 Subject: [PATCH 522/536] Update BestPracticeAnalyser.jsx --- src/views/tenant/standards/BestPracticeAnalyser.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/tenant/standards/BestPracticeAnalyser.jsx b/src/views/tenant/standards/BestPracticeAnalyser.jsx index a0e1cb00b101..692c39478591 100644 --- a/src/views/tenant/standards/BestPracticeAnalyser.jsx +++ b/src/views/tenant/standards/BestPracticeAnalyser.jsx @@ -61,7 +61,7 @@ const RefreshAction = ({ singleTenant = false, refreshFunction = null }) => { ), onConfirm: () => execBestPracticeAnalyser({ - path: 'api/BestPracticeAnalyser_OrchestrationStarter', + path: 'api/ExecBPA', params: params, }), }) From 94eb15fe07074521254b2f36cf0b2c254b76fc4c Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Fri, 30 Aug 2024 01:11:46 +0200 Subject: [PATCH 523/536] alert update --- src/data/alerts.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/data/alerts.json b/src/data/alerts.json index 13f18265caf4..fe03d7b6a968 100644 --- a/src/data/alerts.json +++ b/src/data/alerts.json @@ -19,6 +19,11 @@ "label": "Alert on changed admin Passwords", "recommendedRunInterval": "30m" }, + { + "name": "InactiveLicensedUsers", + "label": "Alert on licensed users that have not logged in for 90 days", + "recommendedRunInterval": "1d" + }, { "name": "QuotaUsed", "label": "Alert on % mailbox quota used", From 529277ea490bfe67409f89a8f267783737e8e67b Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 29 Aug 2024 22:14:20 -0400 Subject: [PATCH 524/536] Improve edit mailbox page --- src/store/api/app.js | 1 + .../administration/EditMailboxPermissions.jsx | 180 ++++++++++++++---- 2 files changed, 143 insertions(+), 38 deletions(-) diff --git a/src/store/api/app.js b/src/store/api/app.js index 3eaa06328f84..bb2e4bb45ce2 100644 --- a/src/store/api/app.js +++ b/src/store/api/app.js @@ -139,4 +139,5 @@ export const { useLazyGenericPostRequestQuery, useLazyGenericGetRequestQuery, useGenericGetRequestQuery, + useGenericPostRequestQuery, } = appApi diff --git a/src/views/email-exchange/administration/EditMailboxPermissions.jsx b/src/views/email-exchange/administration/EditMailboxPermissions.jsx index ff1e7d2b64ce..efd01a155d52 100644 --- a/src/views/email-exchange/administration/EditMailboxPermissions.jsx +++ b/src/views/email-exchange/administration/EditMailboxPermissions.jsx @@ -14,6 +14,8 @@ import { CForm, CRow, CSpinner, + CLink, + CBadge, } from '@coreui/react' import useQuery from 'src/hooks/useQuery' import { useDispatch } from 'react-redux' @@ -24,14 +26,16 @@ import { useLazyGenericPostRequestQuery, useLazyGenericGetRequestQuery, useGenericGetRequestQuery, + useGenericPostRequestQuery, } from 'src/store/api/app' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { faCircleNotch } from '@fortawesome/free-solid-svg-icons' import { useListMailboxDetailsQuery, useListMailboxPermissionsQuery } from 'src/store/api/mailbox' -import { CellBoolean, CippDatatable } from 'src/components/tables' +import { CellBadge, CellBoolean, CippDatatable } from 'src/components/tables' import DatePicker from 'react-datepicker' import 'react-datepicker/dist/react-datepicker.css' import PropTypes from 'prop-types' +import Skeleton from 'react-loading-skeleton' const formatter = (cell, warning = false, reverse = false, colourless = false) => CellBoolean({ cell, warning, reverse, colourless }) @@ -196,7 +200,9 @@ const MailboxPermissions = () => { Endpoint: 'users', TenantFilter: tenantDomain, $filter: "assignedLicenses/$count ne 0 and accountEnabled eq true and userType eq 'Member'", + $select: 'id,displayName,userPrincipalName', $count: true, + $orderby: 'displayName', }, }) @@ -258,8 +264,8 @@ const MailboxPermissions = () => { label="Remove Full Access" disabled={formDisabled} values={users?.Results?.map((user) => ({ - value: user.mail, - name: `${user.displayName} - ${user.mail} `, + value: user.userPrincipalName, + name: `${user.displayName} - ${user.userPrincipalName} `, }))} placeholder={!usersIsFetching ? 'Select user' : 'Loading...'} name="RemoveFullAccess" @@ -272,8 +278,8 @@ const MailboxPermissions = () => { label="Add Full Access - Automapping Enabled" disabled={formDisabled} values={users?.Results?.map((user) => ({ - value: user.mail, - name: `${user.displayName} - ${user.mail} `, + value: user.userPrincipalName, + name: `${user.displayName} - ${user.userPrincipalName} `, }))} placeholder={!usersIsFetching ? 'Select user' : 'Loading...'} name="AddFullAccess" @@ -286,8 +292,8 @@ const MailboxPermissions = () => { label="Add Full Access - Automapping Disabled" disabled={formDisabled} values={users?.Results?.map((user) => ({ - value: user.mail, - name: `${user.displayName} - ${user.mail} `, + value: user.userPrincipalName, + name: `${user.displayName} - ${user.userPrincipalName} `, }))} placeholder={!usersIsFetching ? 'Select user' : 'Loading...'} name="AddFullAccessNoAutoMap" @@ -300,8 +306,8 @@ const MailboxPermissions = () => { label="Add Send-as permissions" disabled={formDisabled} values={users?.Results?.map((user) => ({ - value: user.mail, - name: `${user.displayName} - ${user.mail} `, + value: user.userPrincipalName, + name: `${user.displayName} - ${user.userPrincipalName} `, }))} placeholder={!usersIsFetching ? 'Select user' : 'Loading...'} name="AddSendAs" @@ -314,8 +320,8 @@ const MailboxPermissions = () => { label="Remove Send-as permissions" disabled={formDisabled} values={users?.Results?.map((user) => ({ - value: user.mail, - name: `${user.displayName} - ${user.mail} `, + value: user.userPrincipalName, + name: `${user.displayName} - ${user.userPrincipalName} `, }))} placeholder={!usersIsFetching ? 'Select user' : 'Loading...'} name="RemoveSendAs" @@ -328,8 +334,8 @@ const MailboxPermissions = () => { label="Add Send On Behalf permissions" disabled={formDisabled} values={users?.Results?.map((user) => ({ - value: user.mail, - name: `${user.displayName} - ${user.mail} `, + value: user.userPrincipalName, + name: `${user.displayName} - ${user.userPrincipalName} `, }))} placeholder={!usersIsFetching ? 'Select user' : 'Loading...'} name="AddSendOnBehalf" @@ -342,8 +348,8 @@ const MailboxPermissions = () => { label="Remove Send On Behalf permissions" disabled={formDisabled} values={users?.Results?.map((user) => ({ - value: user.mail, - name: `${user.displayName} - ${user.mail} `, + value: user.userPrincipalName, + name: `${user.displayName} - ${user.userPrincipalName} `, }))} placeholder={!usersIsFetching ? 'Select user' : 'Loading...'} name="RemoveSendOnBehalf" @@ -587,6 +593,9 @@ const MailboxForwarding = () => { Endpoint: 'users', TenantFilter: tenantDomain, $filter: "userType eq 'Member' and proxyAddresses/$count ne 0", + $select: 'id,displayName,userPrincipalName', + $count: true, + $orderby: 'displayName', }, }) useEffect(() => { @@ -664,8 +673,8 @@ const MailboxForwarding = () => { multi={true} disabled={formDisabled} values={users?.Results?.map((user) => ({ - value: user.mail, - name: `${user.displayName} - ${user.mail} `, + value: user.userPrincipalName, + name: `${user.displayName} - ${user.userPrincipalName} `, }))} placeholder={!usersIsFetching ? 'Select user' : 'Loading...'} name="ForwardInternal" @@ -759,31 +768,126 @@ const ForwardingSettings = () => { const query = useQuery() const userId = query.get('userId') const tenantDomain = query.get('tenantDomain') - const { data: details, isFetching, error } = useListMailboxDetailsQuery({ userId, tenantDomain }) - const content = [ - { - heading: 'Forward and Deliver', - body: formatter(details?.ForwardAndDeliver, false, false, true), - }, - { - heading: 'Forwarding Address', - body: details?.ForwardingAddress ? details?.ForwardingAddress : 'N/A', + const [content, setContent] = useState([]) + const { + data: details, + isFetching, + isSuccess, + error, + } = useGenericPostRequestQuery({ + path: `/api/ListExoRequest?Cmdlet=Get-Mailbox&TenantFilter=${tenantDomain}&Select=ForwardingAddress,ForwardingSmtpAddress,DeliverToMailboxAndForward`, + values: { Identity: userId }, + }) + + const { + data: users = [], + isFetching: usersIsFetching, + isSuccess: usersSuccess, + error: usersError, + } = useGenericGetRequestQuery({ + path: '/api/ListGraphRequest', + params: { + Endpoint: 'users', + TenantFilter: tenantDomain, + $filter: "userType eq 'Member' and proxyAddresses/$count ne 0", + $select: 'id,displayName,userPrincipalName', + $count: true, }, - ] + }) + + useEffect(() => { + if (usersSuccess && isSuccess) { + if (details?.Results?.ForwardingAddress) { + var user = null + if ( + details?.Results?.ForwardingAddress.match( + /^[A-Fa-f0-9]{8}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{12}$/, + ) + ) { + const userId = details?.Results?.ForwardingAddress + user = users?.Results?.find((u) => u.id === userId) + } + if (user) { + setContent([ + { + heading: 'Forward and Deliver', + body: formatter(details?.Results?.DeliverToMailboxAndForward, false, false, true), + }, + { + heading: 'Forwarding Address', + body: ( + <> + + Internal + + {user.displayName} + + ), + }, + ]) + } else { + setContent([ + { + heading: 'Forward and Deliver', + body: formatter(details?.Results?.DeliverToMailboxAndForward, false, false, true), + }, + { + heading: 'Forwarding Address', + body: ( + <> + + Internal + + {details?.Results?.ForwardingAddress} + + ), + }, + ]) + } + } else if (details?.Results?.ForwardingSmtpAddress) { + var smtpAddress = details?.Results?.ForwardingSmtpAddress.replace('smtp:', '') + setContent([ + { + heading: 'Forward and Deliver', + body: formatter(details?.Results?.DeliverToMailboxAndForward, false, false, true), + }, + { + heading: 'Forwarding Address', + body: ( + <> + + External + + {smtpAddress} + + ), + }, + ]) + } + } else if (usersIsFetching || isFetching) { + setContent([ + { + heading: 'Forward and Deliver', + body: , + }, + { + heading: 'Forwarding Address', + body: , + }, + ]) + } + }, [users, details, usersSuccess, isSuccess]) return ( - {isFetching && } - {!isFetching && ( - - {content.map((item, index) => ( -
    -
    {item.heading}
    -

    {item.body}

    -
    - ))} -
    - )} + + {content.map((item, index) => ( +
    +
    {item.heading}
    +

    {item.body}

    +
    + ))} +
    ) } From 90b09c7ab1f86d32c6b240d0b9b825ec56599ba4 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 29 Aug 2024 22:55:46 -0400 Subject: [PATCH 525/536] Update settings panel on save for fwd and ooo --- .../administration/EditMailboxPermissions.jsx | 160 ++++++++++++++---- 1 file changed, 127 insertions(+), 33 deletions(-) diff --git a/src/views/email-exchange/administration/EditMailboxPermissions.jsx b/src/views/email-exchange/administration/EditMailboxPermissions.jsx index efd01a155d52..bb6613f63d33 100644 --- a/src/views/email-exchange/administration/EditMailboxPermissions.jsx +++ b/src/views/email-exchange/administration/EditMailboxPermissions.jsx @@ -46,6 +46,8 @@ const MailboxSettings = () => { const userId = query.get('userId') const tenantDomain = query.get('tenantDomain') const [active, setActive] = useState(1) + const [forwardingRefresh, setForwardingRefresh] = useState('0') + const [oooRefresh, setOooRefresh] = useState('0') const columnsCal = [ { name: 'User', @@ -126,12 +128,20 @@ const MailboxSettings = () => { - + + setForwardingRefresh((Math.random() + 1).toString(36).substring(7)) + } + /> - + + setOooRefresh((Math.random() + 1).toString(36).substring(7)) + } + /> @@ -162,12 +172,20 @@ const MailboxSettings = () => { )} {active === 3 && ( <> - + )} {active === 4 && ( <> - + )} @@ -566,7 +584,7 @@ const CalendarPermissions = () => { ) } -const MailboxForwarding = () => { +const MailboxForwarding = ({ refreshFunction }) => { const dispatch = useDispatch() let query = useQuery() const userId = query.get('userId') @@ -622,7 +640,9 @@ const MailboxForwarding = () => { disableForwarding: values.forwardOption === 'disabled', } //window.alert(JSON.stringify(shippedValues)) - genericPostRequest({ path: '/api/ExecEmailForward', values: shippedValues }) + genericPostRequest({ path: '/api/ExecEmailForward', values: shippedValues }).then(() => { + refreshFunction() + }) } const initialState = { ...user, @@ -670,7 +690,6 @@ const MailboxForwarding = () => { {values.forwardOption === 'internalAddress' && ( ({ value: user.userPrincipalName, @@ -763,19 +782,24 @@ const MailboxForwarding = () => { ) } +MailboxForwarding.propTypes = { + refreshFunction: PropTypes.func, +} -const ForwardingSettings = () => { +const ForwardingSettings = ({ refresh }) => { const query = useQuery() const userId = query.get('userId') const tenantDomain = query.get('tenantDomain') const [content, setContent] = useState([]) + const [showLoading, setShowLoading] = useState(false) + const [currentRefresh, setCurrentRefresh] = useState('') const { data: details, isFetching, isSuccess, error, } = useGenericPostRequestQuery({ - path: `/api/ListExoRequest?Cmdlet=Get-Mailbox&TenantFilter=${tenantDomain}&Select=ForwardingAddress,ForwardingSmtpAddress,DeliverToMailboxAndForward`, + path: `/api/ListExoRequest?Cmdlet=Get-Mailbox&TenantFilter=${tenantDomain}&Select=ForwardingAddress,ForwardingSmtpAddress,DeliverToMailboxAndForward&refresh=${refresh}`, values: { Identity: userId }, }) @@ -796,8 +820,13 @@ const ForwardingSettings = () => { }) useEffect(() => { + if (refresh !== currentRefresh) { + setShowLoading(false) + setCurrentRefresh(refresh) + } + if (usersSuccess && isSuccess) { - if (details?.Results?.ForwardingAddress) { + if (details?.Results?.ForwardingAddress !== null) { var user = null if ( details?.Results?.ForwardingAddress.match( @@ -844,7 +873,7 @@ const ForwardingSettings = () => { }, ]) } - } else if (details?.Results?.ForwardingSmtpAddress) { + } else if (details?.Results?.ForwardingSmtpAddress !== null) { var smtpAddress = details?.Results?.ForwardingSmtpAddress.replace('smtp:', '') setContent([ { @@ -863,8 +892,19 @@ const ForwardingSettings = () => { ), }, ]) + } else { + setContent([ + { + heading: 'Forward and Deliver', + body: formatter(details?.Results?.DeliverToMailboxAndForward, false, false, true), + }, + { + heading: 'Forwarding Address', + body: 'N/A', + }, + ]) } - } else if (usersIsFetching || isFetching) { + } else if ((isFetching || usersIsFetching) && showLoading === false) { setContent([ { heading: 'Forward and Deliver', @@ -875,8 +915,19 @@ const ForwardingSettings = () => { body: , }, ]) + setShowLoading(true) } - }, [users, details, usersSuccess, isSuccess]) + }, [ + refresh, + currentRefresh, + users, + details, + usersSuccess, + isSuccess, + isFetching, + usersIsFetching, + showLoading, + ]) return ( @@ -888,11 +939,24 @@ const ForwardingSettings = () => { ))}
    + + setCurrentRefresh((Math.random() + 1).toString(36).substring(7))} + color="primary" + variant="ghost" + className="float-end" + > + + +
    ) } +ForwardingSettings.propTypes = { + refresh: PropTypes.string, +} -const OutOfOffice = () => { +const OutOfOffice = ({ refreshFunction }) => { const dispatch = useDispatch() let query = useQuery() const userId = query.get('userId') @@ -932,7 +996,9 @@ const OutOfOffice = () => { ExternalMessage: values.ExternalMessage ? values.ExternalMessage : '', } //window.alert(JSON.stringify(shippedValues)) - genericPostRequest({ path: '/api/ExecSetOoO', values: shippedValues }) + genericPostRequest({ path: '/api/ExecSetOoO', values: shippedValues }).then(() => { + refreshFunction() + }) } const initialState = { ...user, @@ -1049,18 +1115,26 @@ const OutOfOffice = () => { ) } -const OutOfOfficeSettings = () => { +const OutOfOfficeSettings = ({ refresh }) => { const query = useQuery() const userId = query.get('userId') const tenantDomain = query.get('tenantDomain') const tenantFilter = tenantDomain + const [currentRefresh, setCurrentRefresh] = useState('') + + useEffect(() => { + if (refresh !== currentRefresh) { + setCurrentRefresh(refresh) + } + }, [refresh, currentRefresh, setCurrentRefresh]) + const { data: details, isFetching, error, } = useGenericGetRequestQuery({ path: '/api/ListOoO', - params: { userId, tenantFilter }, + params: { userId, tenantFilter, currentRefresh }, }) const combinedRegex = /(<([^>]+)>)|| /gi const content = [ @@ -1087,22 +1161,42 @@ const OutOfOfficeSettings = () => { ] return ( - {isFetching && ( - - Loading - - )} - {!isFetching && ( - - {content.map((item, index) => ( -
    -
    {item.heading}
    -

    {item.body}

    -
    - ))} -
    - )} - {error && Could not connect to API: {error.message}} + + {isFetching && ( + <> + {content.map((item, index) => ( +
    +
    {item.heading}
    +

    + +

    +
    + ))} + + )} + {!isFetching && ( + <> + {content.map((item, index) => ( +
    +
    {item.heading}
    +

    {item.body}

    +
    + ))} + + )} + + {error && Could not connect to API: {error.message}} +
    + + setCurrentRefresh((Math.random() + 1).toString(36).substring(7))} + color="primary" + variant="ghost" + className="float-end" + > + + +
    ) } From 423ed7e4254b32ff9fdd89a399792f9fc0c64b83 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 29 Aug 2024 23:10:27 -0400 Subject: [PATCH 526/536] Update EditMailboxPermissions.jsx --- .../administration/EditMailboxPermissions.jsx | 63 +++++++++---------- 1 file changed, 29 insertions(+), 34 deletions(-) diff --git a/src/views/email-exchange/administration/EditMailboxPermissions.jsx b/src/views/email-exchange/administration/EditMailboxPermissions.jsx index bb6613f63d33..9283001ca1f3 100644 --- a/src/views/email-exchange/administration/EditMailboxPermissions.jsx +++ b/src/views/email-exchange/administration/EditMailboxPermissions.jsx @@ -791,7 +791,6 @@ const ForwardingSettings = ({ refresh }) => { const userId = query.get('userId') const tenantDomain = query.get('tenantDomain') const [content, setContent] = useState([]) - const [showLoading, setShowLoading] = useState(false) const [currentRefresh, setCurrentRefresh] = useState('') const { data: details, @@ -799,7 +798,7 @@ const ForwardingSettings = ({ refresh }) => { isSuccess, error, } = useGenericPostRequestQuery({ - path: `/api/ListExoRequest?Cmdlet=Get-Mailbox&TenantFilter=${tenantDomain}&Select=ForwardingAddress,ForwardingSmtpAddress,DeliverToMailboxAndForward&refresh=${refresh}`, + path: `/api/ListExoRequest?Cmdlet=Get-Mailbox&TenantFilter=${tenantDomain}&Select=ForwardingAddress,ForwardingSmtpAddress,DeliverToMailboxAndForward&refresh=${currentRefresh}`, values: { Identity: userId }, }) @@ -821,7 +820,6 @@ const ForwardingSettings = ({ refresh }) => { useEffect(() => { if (refresh !== currentRefresh) { - setShowLoading(false) setCurrentRefresh(refresh) } @@ -904,42 +902,39 @@ const ForwardingSettings = ({ refresh }) => { }, ]) } - } else if ((isFetching || usersIsFetching) && showLoading === false) { - setContent([ - { - heading: 'Forward and Deliver', - body: , - }, - { - heading: 'Forwarding Address', - body: , - }, - ]) - setShowLoading(true) } - }, [ - refresh, - currentRefresh, - users, - details, - usersSuccess, - isSuccess, - isFetching, - usersIsFetching, - showLoading, - ]) + }, [refresh, currentRefresh, users, details, usersSuccess, isSuccess]) return ( - - {content.map((item, index) => ( -
    -
    {item.heading}
    -

    {item.body}

    -
    - ))} + + {isFetching || usersIsFetching ? ( + <> +
    +
    Forward and Deliver
    +

    + +

    +
    +
    +
    Forwarding Address
    +

    + +

    +
    + + ) : ( + <> + {content.map((item, index) => ( +
    +
    {item.heading}
    +

    {item.body}

    +
    + ))} + + )}
    - + setCurrentRefresh((Math.random() + 1).toString(36).substring(7))} color="primary" From 6495cb15b3d68fd6275ed9886557c20d65768cbd Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 29 Aug 2024 23:11:43 -0400 Subject: [PATCH 527/536] add proptypes --- .../administration/EditMailboxPermissions.jsx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/views/email-exchange/administration/EditMailboxPermissions.jsx b/src/views/email-exchange/administration/EditMailboxPermissions.jsx index 9283001ca1f3..bddce9e02628 100644 --- a/src/views/email-exchange/administration/EditMailboxPermissions.jsx +++ b/src/views/email-exchange/administration/EditMailboxPermissions.jsx @@ -1109,6 +1109,9 @@ const OutOfOffice = ({ refreshFunction }) => { ) } +OutOfOffice.propTypes = { + refreshFunction: PropTypes.func, +} const OutOfOfficeSettings = ({ refresh }) => { const query = useQuery() @@ -1195,3 +1198,6 @@ const OutOfOfficeSettings = ({ refresh }) => {
    ) } +OutOfOfficeSettings.propTypes = { + refresh: PropTypes.string, +} From 9eb600c5a1fe70bcd66a78a616a187a61bc22614 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Fri, 30 Aug 2024 13:44:07 +0200 Subject: [PATCH 528/536] added get bitlocker key --- src/views/identity/administration/Devices.jsx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/views/identity/administration/Devices.jsx b/src/views/identity/administration/Devices.jsx index 664f7552757c..f7808c39034a 100644 --- a/src/views/identity/administration/Devices.jsx +++ b/src/views/identity/administration/Devices.jsx @@ -36,6 +36,13 @@ const DevicesList = () => { modalUrl: `/api/ExecDeviceDelete?TenantFilter=${tenant.defaultDomainName}&ID=${row.id}&Action=Enable`, modalMessage: 'Are you sure you want to enable this device.', }, + { + label: 'Retrieve Bitlocker Keys', + color: 'info', + modal: true, + modalUrl: `/api/ExecGetRecoveryKey?TenantFilter=${tenant.defaultDomainName}&GUID=${row.id}`, + modalMessage: 'Are you sure you want to retrieve the Bitlocker keys?', + }, { label: 'Disable Device', color: 'info', From cb32a993ed4aa1e60240ab7cb09802f79cd1a1c2 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Fri, 30 Aug 2024 09:01:59 -0400 Subject: [PATCH 529/536] Move entire request to post body --- .../administration/EditMailboxPermissions.jsx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/views/email-exchange/administration/EditMailboxPermissions.jsx b/src/views/email-exchange/administration/EditMailboxPermissions.jsx index bddce9e02628..740aa2417455 100644 --- a/src/views/email-exchange/administration/EditMailboxPermissions.jsx +++ b/src/views/email-exchange/administration/EditMailboxPermissions.jsx @@ -798,8 +798,14 @@ const ForwardingSettings = ({ refresh }) => { isSuccess, error, } = useGenericPostRequestQuery({ - path: `/api/ListExoRequest?Cmdlet=Get-Mailbox&TenantFilter=${tenantDomain}&Select=ForwardingAddress,ForwardingSmtpAddress,DeliverToMailboxAndForward&refresh=${currentRefresh}`, - values: { Identity: userId }, + path: `/api/ListExoRequest`, + values: { + TenantFilter: tenantDomain, + Cmdlet: 'Get-Mailbox', + cmdParams: { Identity: userId }, + Select: 'ForwardingAddress,ForwardingSmtpAddress,DeliverToMailboxAndForward', + refresh: currentRefresh, + }, }) const { From 2e15063054f1c58f386cb1bf1d3e8cac7516386a Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Fri, 30 Aug 2024 15:47:46 +0200 Subject: [PATCH 530/536] Right of Boom logo --- public/img/RoB-light.svg | 17 +++++++++++++++++ public/img/RoB.svg | 25 +++++++++++++++++++++++++ public/img/datto.png | Bin 15316 -> 0 bytes src/components/layout/AppFooter.jsx | 10 +++++++--- 4 files changed, 49 insertions(+), 3 deletions(-) create mode 100644 public/img/RoB-light.svg create mode 100644 public/img/RoB.svg delete mode 100644 public/img/datto.png diff --git a/public/img/RoB-light.svg b/public/img/RoB-light.svg new file mode 100644 index 000000000000..0673b2a8a449 --- /dev/null +++ b/public/img/RoB-light.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/img/RoB.svg b/public/img/RoB.svg new file mode 100644 index 000000000000..d188e1eb541b --- /dev/null +++ b/public/img/RoB.svg @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/img/datto.png b/public/img/datto.png deleted file mode 100644 index b0fad6f50233e46eed52acb6d6c5e037aabecfda..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15316 zcma)jQ*gsyxuC+>EcU8E8 zoH!yJHXH~D2%@Bfh|)hk_K&n+p#H5WEDopt7^tI?xDZJ7H163y0m58RRuBZF4ha8g z2>DNjwUf|v1OY)B_#Xltu`M+L0pYfk6cJQ$)4TG9_ETMIdhR-DKEn%P62>fqp%fKR ztQA)@1~rb7mt{VezQgkVa|anVcS;aV2CFewTIa`_A3z+aB$|#E$HgdPnriqyNq3U@ z^Rdi#;$VJ^B-4=l(3jRQv2In<^{myghLd{9z!&uH?~yO`bTq_1KpExU*S(kFUC2?Sx(0b+Sxuv(r4x6 zxyETpPgw)@7o>2wr};8yjCdu&(E%gO&B>{->Qg%&U%tnczIp9@Pq&-SkiHdvXDJA( z;oosXELTaq&G(l$JlA#t3 zW<@X4_O5}?Pd%r>bik~q%kq16K8Bo@rpE8{`w)f7g|hIAj8-fFWT66d@4MZYHTUaW z(+YgsT1;orG@_Sm*+lz=Mo+l3LG!*-a5^`i zs<-s)5$TS=Y5ObqH!fDIjk#iA$HLs)k=jC00%`@lawhUvNC+o9htiiTwyxa0Y)~Ga zX+qQ*1ZoVrCHt<^ZGr?}YS(CnpbR5k^{#dhJ;fUom~>UaDkU2g;GUKB(iO%DjRr=z z7azh^)jlOsB!5EkpY0jv{x7!M>JVQ{!33v2_0H+Bg$M2~b}*4pQSg^jW@>NFO>HAw zp`=fdDAZaGU&@tUt6-t|{A=R0#I`dbcg_%tggsr{4$b&nM5N~9w3fTFD28N4qMI$t z>>S1}5Wfll^hXRO7*rEdX;d+fuM>bBZ)oIiIsfC*pROalz+}rI?L)YnCqFLtA8VZ|_Wn6UJ?Z+Rdw9Nw{2Gq)Upv z;=RB1(3@Zhrn0f?<72{yG9vbOXv(>dp03Ee3;u;!;6Bg>yqAE?heiPB*vm4$sz zJ0sstV-^85e3W#5_3PEOU2@r8_k86qyZ10D%&(xhn-h}7K{I&g?N8#tNmfo&5r$xF zxOBMGnDvxb0&kI~B%HX5J6k&T$%pdWbcfxW+L_-;r~bl(I0(yaodt9g?*%15PURju zx8Z^-qqNL)Iqe>2;x2p{9bqQCs6%>ULGf3o0O9e72Ckf}h#XaYt||>iajC=O{8pV- zzp1VVG$sGAa~e*o#el6^{B2o)=~H42U8CzOp?IwE`3SEwF5p8E2yAvX#Ch4`bT8>_ z|C(*RzT2MYp{z>NQNp}eS}BDs4B_Dp`8A#zgSvo9<5B3Q_rRVy@t42gMSKCO1T!!U zxEzF-S60m(!LTJsoK*jJi)&`9!|P!DTo}2MrWD%{TZ>qW!UY8T?AL!{mteZUKSB^r zoU$Z+SLIyh3hOx|rbGOW8-;vF*P0V%{q<&4(^DDtEEM61XWuU`FCH%fN3|S4mx~c- zq^$GVE!fyG3goVF@ZeKYKt&itMhTe#n(Jw4{B!<1RsES=f%)D>Q-LR0b;o}q!#(DY z6F!{G3z~uNEQ>1^rFmVzA8DMN*EYdv2lwUJ*BdUSE;xSvSO!Z`#18`FvGM7 z7ssrToFWi1_{rVka9Ko~A>|v?OWWL2C?^3)#- zEK6(e@3{HeuFcDmv6{c`5mpW??#6c(un@=tXiBaks%!2{s3*o6$ML>bWb7D=zoDL>U`f?!CRtK%^CbGkF&*N-Je-+Kc zf7NC87v4^IMiI}3?u%hz)%#3KBrEGNJ@$r357L`tdLjY?}1Xlk-m;d`;cQyYBiBOJ^dI;s2=0 zPxOC_sSsXpge&)`O&!un=I+5r_p+6P_YESuEbDy&Sw(Mt-U>SJ8ab1wVxL_h^dk~h zh4!m|zP~nMU1`^!3kPqSge z|H-p}Zzd?mkJZ+5)hyyi8)VaW4=pn_cziB!YPmnq@72P*hpbvO}d z)I#Q@yvu9rW4PUQ>6(TwMqW`elCLJ0hdP&w2E$1ken-!*}hhEw*D5$ItRu| z5`!uYq-U#kRB7T!x=3S#zhNhv?jE6R5g4Mw&Vf4zUe*e-gU)vWeYI{!_`Mr{&cIcj zzc|JI)6lZFiw%+ZqJ-}Y0wx+tawEq=(9q-M#g`%?;f4N(Gd%rOXmOWV!loDW?glVG zzXXi_OawfkGG(636D=^GgDak`)RbubX8f|_H zrE>m!c{LF!>;0w8c>s${GaimfP%J?eQ~x&yoEF&BwUNd{y-);NO~wyAB(C06r0Q4G(2pX zODUx_HBM41JSm20RmCEFgdsma{UedbiLyw1abrNw>Yh*=P}$|KGg`fbT<$j|*I;L| zle1d?(`ZRmdM4y9i$fVx!bK1~Y;YVa?^Kgt?QGxmY~E|dgIacFq@E|5HhC;IMN`w3 zTt!=i{k{{4YDe!U&+Utpc{P?-87N2UrRqx~X^n}mL>?w9XxoEK86Fu8n!*}@wa_d{ z`UPr8FPqPD_?wA&p>0Pg5Y+j5xOtwFY{~zd_!r{Vw*0Tfq&_}EWamzL;)0)pt(g07 zs%{(0x@=+#xaZt;891uYX1g%OP+o|2qqCyIZla5!s*fB|;`J>P7Aul`mX3qe2Bx1w z{=>@+1=N7kSot*sY%r2q@!x+K#$yH#0k%H%BVJ$SioBRP{G}Vk@lAKoo>@+mf4$p& z3f(iLX1G;$>=Z4df6$x)9=%C(lI<#IBz@klry~hI3Sa2}upCr+Jja)5hg=4y$M@2q zW|eIoCg&Fk^$YN12?|;CgAKoMx~~jg#MLps-x}iA30Q(A-If$b4M0?uWpRTG?x=VV zzPq1?>w1WhBlsQ3>-)k|t41MzIyhd|LzmG$Blqm`5pTntt%g%83nOP%J?Hb+8qyP1 zQ#m7-W*jE7d9|@%&$D_2-e#iCnDB%4(z}SEvCOsRU`8)@Im$e}=7#k%=QpIRgU;YF zEC!lo*+Y?CIf8F~^K#aBn4Un|-cWi=vm>&btC2fUDmDC}qQuBbAFjelgSD@KE_y%y z@$h=aNr{%2P!~ZGpN{E&Jn1RrJlVoKVozLwo)IcARm&eJWhKRfd|)mwTA+NHn|y#O z`ntQa3fU44CG++aNaIFSnGCQ&nz@Qx>DE)Z>Iob1NRJZuEd?qN8j@?QQ=OjBJ~YkA zP4m^=qqA6&UlcMqITo#%qwaWi9}w|z0cNI;0$axVE;3NZN3r)6Rl?`_wa`C)Q{u)G z$`pUVCQFdyWh-fTRkBr#T+?8!E+&i~6_dtn{-5fBkJB-8Mg-<^P!z|T9#k7UjVV{B z+{i%_^UP~-(kx8kSoOuTsAskNYXUe!sqyUaZZFD>3y!e|sm#ai2ni;hEZ>HLzi~`q zw&p}~o;Wr)ET^{c=PnTi9_~h@q$wS|G-L4IpBHCDdb1b*{0z^%P zg;C8QKf*cw!~PBf?j>db->_r-m)V@SGU@Lx9ZJ6Awii3j zzsX*8vsd8Err+Ou+n%7;W@hqoGt%0mbH;O9?4S+5@du`(`pz5W`*R>}%QlCN$L2FR zLtK~R;qjaECn#`IO^ob{Xlt%l@jX5gif%^go-P)t#7+rr$)0p~E8^ZUQGyeO8(%YJ z;s7k~(T6!U;>zW}YGW?O2Yb8)L5TArs%WC83P>aeqW_H7fS?cGK;+DhrbZ8h9&8!D zb1?W?!wrkk#<+iO!(C*%KXjw#I^Q7f_-p@P#UBV?jr~5J2)Z>R9sOdhPMuqAhk1wa zmb-$oZQm*e^z@NG%h{mxByoyJ5CGW=?4Vh=c$aoocqz-yTeEA7mBewC3!Nt&nZ5qw z%YPTK$1s!4h5i9tW;K{@B+Bq%I*nJ*li`j44d}AO;@YFnm`>V8TdDD|fOUcL^+I;&E1tfhq-|!KUEXJdHSF4-s@&Zb zb0jbF|2IlHXzqXb+nxm+=I-T12EzNC;rTDEjP zyXVC2Jt}3MqS%{6)?fc58=ds66Vy#%o55?su?Fw4d=$H%$XsMNtV&s8nq|hiS;znf zPm18E^%8+$N7B#M_jBLn%B%YE7K%+6S)2)}s13@+WvCuxfR!M_Cjkxor|65M#>aJ7 zELFAtIvBe)>`3UAo8-$CH}d2}2g3$xRchIK=smaF10HTP8c$MAzN{vUsa~qxUGA+a zPJW0fZ0{O2;P^FLd;ZhbE7D(1-s_Kx{cEYbgS(;;ul^?>I2t||Wt z>Z+RQRR7e4>r3h;nU_TiT^Oo_#CdH`*t;r-El-&?N^Gd2vD*-VKQjUe&L%Z&Ec+)` zt*8mPp_n7;-7iggk5kSWqM6nO#|1m&CPwtbC-!M2iCsSJW$mGa?`|#T(Ni5eA^V-> zX4v(a*2B$aZ(!nCZBqjiL09{Dg<-7bF(+ADk-xT<>sQT#F#8@hWc*ydL$~UxmSLeK z5p%fgu@rT>8l#@t_j%N|c9liw?L!>zODl_duy^|su7oR>62=w%B|{w>?M;{*UdTfQ zI5^31jM;6g?o$Pl%B4knwKX!!Wq{$L17KEha_2vf8TY>vK@)mw&o#<7+TOQw&X5d= z%jZ8jaq3S6(L<_%YIk?tN(Sp^J>?Vs71O7VN6` z)LKp^R31-;(*zX0qDvULNAr{*p*-QsqS2Xym|mwvD4Cc1O|2(T6CCI>IeQdr-kPtY zr|#he9b?kgEOb=ngNuL4HKe@p+=gIl7l^ru^O4Rx^W3h8h&OwFWz=3f!Pn>fS%)vV^p$#zavupMt z8K0||mx{;N#Ll2S6oecrz<2-N+Gi$P9u{o>=aRXu_fL@LsBsp%?Ftq6Nm;nHC|qC- zty~(EIns>_5z3wY%3BCbpiH3+%awL1QF`vZ}bh+8lC#!<_4$cBCA-O1Jr((gqRs6(@POV~CcIQxGeD;+BVt$}EW$64{rP_tZRZ(v!Kx zrw8PqVkn}xo4vj{FBC*=#T-2r*eqmW+%;y?2Wn@vFc#lUwn>haa@0g+pbZT0hHEi_ zauKrT*c$@9+48}QwwD*dy}uQ*b_2mD)NilGs!f#ieY18e`JDarQt1;@bZJTtenIV) z9Rj*=&{TRrdkrG(%&uyT|8b!U{X3zJHa}_N;9lhq9tcr-&pS%$dM3%xV{2v;AJBzx z+uVn%NdEp{1$b`1Kqfpt6LrTG5`B%%{{H@Ftzt`|YBY938uzHceAzF_UE& z;ol|BOy1gY$Ig&{P9aQV`}7FYWvv;i2@HX!c+SZ>3sF=Ruy<`7G3knZaBOI57|qTY zL|j9XmttlVLsdy57Wph`p?IKMVR#!fbva3ke`z&p{TOf~@@{QrYusPX`xh#}J-B*a zhmBk`pWHgMW|(kK{!C0?COAyM_hd0TEy<5;f9yFMtLbxBdj7TqmYcMP(YEAM^|rTw zedcGOTZ7tMpCA>`6|+(?e=888@mMn$7dR_w3YXr{;J_VG3~v~>#<`szeDU()n{&Ai z&GD5ON30ZKm6(-4tRKW?u_eG{u7}?5iYAglo1U0m#qNCi|IaNsc|aUG-YF}rbu-p} zLneI-3i<>m*Nco&4)E`*)K45y5-c;Hb&|11WlofM(#5L<;LUbk1+Y0qXCgL29M`7T z+`?kMSN65AHKL$GnX9<#TvH=)o%=o<1LR1K&Jf8+D*r{v=VReOa3XOBX>hco+jczk z6oi?{pLH`7wMm+udXEh0<7G+=(6v@|ZFLRr=gg_|FNrI$ne6QS^>?E4fr90mACID; zOj)W$jd>k0WoFz!W#b(V-=9(0E1a!aynQdl2r(><7&!M!KJ5I8mx(|NgczK+tPSIX z@jNVPlTp8sj(o41mD%Xj`ds^PbQ^XK=2o3)9Os2Sdu$z8jkEie>XoxuMDaCEZ`_Z{ ze|cuX3$(S(jwqe+k&nzbb)0LYS$b$IrZfzg#;{Exb}@-a!ycYF3cJEkXx^T0x~RE3MQ{6%W|lcg*Rtl~So0 zbHP;v>YWxWJu4M_=>L`#PEc$*UCiX3IIr>a9Ld|ZS$T--PHXbIf#n`QpE;1E*g5St z;c7CGVyC9yr=zj_IL+p@O503!`IWU#+T6=K;vR`ew9Z(iL{G}O$6=+@}xh~~Ji zF_(!kehJlDNMbE`QR!Otvra7|t?Lk@x!Qk~v@jEebtUi#YJQlskcn^g3SB zq8CxaLM0sGbhij4%KSG$f04&N;)$;!1qVf)g5b{sgljyTZnx8zH;(5XWEYAC}F6EH7P@G%x9T! zx+5V?F&;@GY66?Q#EG)rs|ViS{o5)o$+R@2&#nqr9RdUStd%+P1) zjZn+Zxc(QlkPS9rG@QoyugCeu`tS3lqBcQ#K0 zqV#dAzuv~bbG~LC-?XpHKYu7{?&yMm(OLtoDH7t%13NKbLdCF)8TRvF?%}4i6FU-?glh$7FrdTBkXkI&r}g4p?fjuX)M?{|BgKqzjA( zjH07J*vz#-&D~24OMifBe@HE2lxAJeeYWby#qreS*wx&8C6jOWS$czm>x- z8!Pff2=lU9#Jjr>lP&t>Z(ik>*UakN_uASvBFD#SxFD#WhJt+tFQ!!~KI+xyh~SPH z-NuT2J>i1o60YP+MS z~E!_rDo!irci}9n5Q=vr&FXpLCScm zoOIHfswKQFW7Z#4;r)M4`4m|*7==q=)H2$AVrxRb!aSjQ9xbo^(?j$=2b0DX`U8VJ zN{~(k6wo?Ym#?ZPje@LFfI3ZlOd%gTKR?B7P&=wtLSnH7EqJ{c#-YHuTlb>Z$hUuOj3!*fJR{irb&SN+>ai z9gDkMieNg}f^Q!htmL~0N(AqU*IjZCeH_dLH(euR9X>;U>YJFW&aMwSXry*A&?hGQ zcAyabmK!OoEbKY_bUP#C_c!@mBVPi*Ob!3}&%(>EN--m37J@~K2v64eqF-u@#o$rUXS}# zWIc42#tQqFPbck`M$xVFzh|O`zb3$-x1ptP_F;3f6J*(!rAvBd6uwpMcxNNvApG)) zx;{FKP#FOE&-KF}8}`HEEm38f4PYPG<6|ch9toZsigwDJr*Wuv_n3o~709QNmOCVISrIZNo_er@Tg`SmN z1|}ig=R>{s%8Z_iK{k);XrD9!_E#X|h^NpRsg)o>Jgp8g=dO@mPGKsmZx5yOcyJ)} z1Lj4c>`Hh0*YQeo3V%LMh_pwlM@dQ8w>vEKC#1RIWO<)X)}1udy3A6!%;{R;`eGxs zH<>jAj32wEIaynCkDcjqs-*Y(=nC^C8@wh?uGSCPZncl9Z5Auz8D}bDx&db(%P$)Y zn~FIFEOf4v=Q0e7RB9GX%3bEsWAh89@mWDT@rxzwlp*Wv5vZiV2|xzki}j1hi5Q-j zh^`TL1hkUK2<}>|FI;*|Yjp6^3g$|Xo_=e?=K=HzT8+1r`Q1op;4n_befeKFKn#n_ zTfU2_#HG9lWsMyKH;qB+t?GQ=*~+nc&dcZVKJv)4?vFK6vRFg_G4;8X2LcDeHuqo% zDzxzUf6jA=DogCq5%BCYtW{bMFWU!Ma&_GU@qy&uRAh zo50VcV>D=Mq}je$U_wD6ExGoWU68rHV{V}}(ZX-;V%Kq;Mbrn&(dSWauT~NkW4Gaq z%%w!ZJt#AyQX#9B=r@SMr1-+3ew9?m-tx}~do>~E)hEzt?qc#qQ?j9>QF+?g*Hp@; zJh?ofNIP{Z>Xe=NMA|u4=ByZDXGGrY4Ry*7-1`$&Vq4A|jU$6&tW}&A+daY#}|tVRNcu2LU|0&{#^wx=G3n3Ex)Bhm~|!&QTJpTGuM8 z0aIDaFSn3^9XZ@AyoP}QPMnZApttalvZ0|!(Ss z7(ENeh2L(sTb_qyzv5wPI%0~_kO=EwVuE)MX1QmG6%a(#!bFS>Ly*w8V>nW6w1PYn zrJJ@pKmIzQa7bA|{~d=M3bBr=%~Y@5|2e{d^(xjVS6GRxH~VvTNqU1+mS8%o>@}AT zz&JM}0_4sIQ-Ow8w>2w$^@>c4UP88Enqk|ojr@H|tkTXCL z*Iw^&Sf-7;)Hl+>rd-O2J7iWIZ~B+5bUHlUB5N|t8sgkA23q(T#LC0It~|VBAf0ua ze;)FMKXfiLj+oWiCPh>!k_u7DsK@kD!lqG(Lh|l+274=e)1W~gONNKRTN-hQ3#7S~ z=ya-mnk>>Jyp!t2Vw{v6id1vKmqVjpB+DtsmuYLPY_2vq&Oy0zbN4&p9!5>eXnf8; zqWUngFhARlQ^z4^m&(5?k~Vi~2dH_hl}2?0LDZU(0E)LkeqOi3dt@H})3As|)d4XX z=RjjPYHf8pAQ&8&pnt32e2h)2x5CuV+QD56L`QqS+xUni&l*VS>}%1HbkhUblAYvD7)?Xvuu@URVcIr#}d zJ2TjhYZ#Vu%uP$h%)YJCHuX-|nXD4?ujAl}(C&#%i7uD_|J6J`qD$-97lrj+a3HqB zVr(+pWLekU_31B_w_8dCg;s_{g$*U{-jwIU#gk65$WL}$piTam*;&*7;}3{Z>6;g) z--Bcm_8=Q~nkl7I288waZk;06;IDaF=LD@HZ_1goPQ-3%@%V={K%in7<5t=Q_eNZ* z*c6j&8!zhfaD27xWW~8pg=0y2%AFm3xb`%*>dxm>CW*Qa@X9$t%H3o*Y~4^it>NXB z+-|jt*fLB3-uM0`$cp(+tv}}MNEG+-PQ%ZY zdm-OZ-=V%PSErK??_?dWuZ!~8upEK*RKp&HJ6FDjaQDU?8Z;JdokBcwSJWLQ7Q^1Y zsqw6!6KC#~@^koersBCQAL#xhhX)tW=JZ?2u5<1A<@}gx&?>J+9>1{GrbAZwt2$9}Z}zkOo#Jw}A7$^~#Gi0;{`hhw*CfxU z2iz-!bGEhpL9Q<6YiiZ%@zs{RvKV0O^zsvpCX`hTGDQQr>!uG}ErOFU8NVP%Nw%I1 zVF|zWciHi>_av}>YEa#IDoOa+fqn~Y zPEz}Ja`rizN+(S?3h@S}a?$Rc%?`>1G-7llAhZ^8V7_8VV}f!hc( z%|zvAadiPZ;5e3xUq3EFH(+5eW+5|l-&iN(CHaH@`>Al-b2=yUYaOsAOhdb)nz-lM zrp&9CgN4A6iSO>3AF%S6&ewv)Q)F(vt%w_x6`IkmJr*Objvk%!Ao;yG2pAa?ROW7~ z=bP;)ReQP3(klCd&&#^4n{nyvtE5Jt;r%tn%l|oWNQ-|U#w!SFy8_%R6|`&4$%<6>z;@4+GK1!!{M#) z(mG!jsMPWzUB1K8rr;w`oEamI&@{A;T0OPOX=++Kx4E9Mib&1J_*6 zo})cQ?oSCf21l`c4t{nXWda^K;Dnc0-_z*JV|0r1C8|9h-%R4{48>L9bNjo41EZ&=qie@vacN0*czcgTXTpBVf-H1yc9xNr z*w>vBt$l6$eSB^yPWM#{WZ;J+O8yrD6iyc;xFoNW?vKn-I=Kaoo%hW6wGifdkePJW z0p4vm+9Y-)i!9C$$H&KsSXo&qv{O8`xd2=QQx)y)TH8rD;qmeD z5Qlf>XiAlZrKMF>SJ56~+H-cB?P5zSD=#~3j^>Lil7h>9rJfMy7SB%*w_`$Y_-c*6 z%gf8#1=*4viM@#35L)W7{ayS!6M9QTd2!hV0#^*uRDeedM`}uu0s}a7Ep`hEE*SG~ zI8gxtgttt8z0Qh34B79Y`xvuD(vEWVqpi@-zkaPas}ze;H&Xgy&tt|xxmPHfiFWmT zvtEYQ(=m8=#}t76^WHCjB6t<3_&1 zK0P5a6m%#3=HVB4FG`%C#zxP@L3&3fa^$?u6m`D>-_iF^0Z_Sd7_w}|HN&_c<=WI% zcTxVZH%q31GFIOB7elRJC&FJap|F@7+72%XY{#qVf55#iF(az;_7AoCMzCcWFZ{Ab zp1+S(4}NCVMiCxviR}rOGbao7Z~TbUPfAI_jI1X}fPODt+i;sL{^%`$Vo}$S9HE%~ zzc#8MVCePc8dlMqQkUH^riWS1X#g`w{Qa#SR=3OeUD%J?qjIq=$&h7%!C z+w7>znk*O;Ivd>Ql$-?PE#cR5pbz-yz%;^lVBEIf;}_kO#0cVSs=lD64nBIp91&ne zgocJ%0M!L0u}p9+7v0p0`ThvUFAijeS#TrQ)*9c4Eo@WFq&-n$lDgd7TyE#YPB%@WObrKP${(rW`j@)^XWy5}5>OOMcgz+e|1^;+_CJ-*8Gq z|6E~2e&i?8p9s5pz8tKY>kV{+nx9Am8g+&z^u7&A?zXCHImrdpfY7-=EVzN1c!xPb z^gGRS&`A5S*rSB~3hHFn90PkaUzHRHNaKHmG8IUhNCmP`oO>Xc4qS2m^&hsdSUs_@ z`;+L&hdo5}L`1_)of}n~AOdy0g=`AFPT&Ruq?pd`k=cvR8qbz<$n8P@ZXoI za2AI@;t2A0=Mmj908}vIO`>DCkx4M%e}nk={_EKg;P>D6`)R(vzc-<2!%~1Yr;llc z%(ZP67iiC7XlT14&E1myj;)zfGr8jc%dfR2y_Ft zPz(50oFBNwnQ9#Ufw5ueSM{&JcMkHMud3b)OJLMfG9r(K`fLeMWm;b+FeD9DLq|hn zap-Q|#i_Fo4S2UqcOk}Z1Vda8gSy}AsVEbPETofgXw#{;w~a_mO`WL=@4 zyW8ip{~!oLYs)1pmzNLdL)r?m;=sx|FZXGC6eA&=E_~iWnOAR zn&=G5#&~Q{=QASR6XeTVLQg8(Wz-z*BCQfPv zGl=yMg4u;*498*7PftqB{@q&%SU+$q5~WyKqtNnx!9 zapm1r`G8JPC;7d05FX5-Vf?e50~7@TDD0WI(tbW-YYRpwlIVxS3L!K|3oo~}V5HFj z)-2OXQhNUgjxDW2u)?bP`ZR*Uq)02Q^KbUowKE!t$a-`CEu+DK5>trN*5n>Y_*ja{ z!UC`%9BLfwklwrMhWJ*}a=%WJzLLR+6=E+g_T=1@CtiOiwI1*nWIKIZvJAv;C_ej5 z7|I`v2fGszIXbX?eNZ2$b=n)uNT^A>&4stTzz4>6UxG)Sd71ts2+ngm@0jPup)ZhX zT^@3%s!KtTJG4gm{m~P{z#*YzD|q`Cdo@8VkT-Zia&SmD2etjZVFE0Q*qIV?6Xd{3 zmBF`~6nGBBWxd$OCaJ_lx)M-umMF8(_Yr#$Xc3g;`OJ@I*J}GPba_LxG(In~K1hI9q;IY&|Ja zurs~IF-@u<5BOh5z~wM?!L|m4Pm$9P#lySPm+;9+nY1Aie<367SSxV0MO(pdb;Hk+ zysVT!Ac5)4a8?aLdPFCfj?Vs9h-oh7G|00IUbEt&C*bJ|_sGbe7x)j^(Y(OSiv4|d zPgNfFX^aQz_x@^yI<~%rOl%D=05DKSQ%K?7O*cv4fNJigK{S3;HM-5#G{l>d!0P=7 z&4N?B1xh6$-Xm>_yyfnB=*ua9(4e?mMhD&AqsXi5uymtpuVaq zFRxG0Gepy@NfO1|+xkJt3hvJzfJ&@(1wj$L#GREn(VAPkqdHi zS_r%&Mu~%s!S2@KslzyB;DD9S;9!Gg^d)3C@$U~vNb}}S-&z9vS+ZG_Xz{^VG9+i! zNn+K)n=!N{M;LMiviTD8|1kB7fg$1rhX@h$v8pMN5Hc9G6=enqc#ykY*VVV0?9J`b z?Yg4h50=Lri?1gzK7Py+-4NsJ6%&mjBQ4ZnaQn6Al)iir2!z^`Oe51m`WqNPUkI)7 z(1ahsDU4*Uzpq2v^itOW=Y6_K4|Eu_Hlg z0C+^T*S8^K1!FHC#k?4dX2C)b2Y0jMrOny9jgdnj*b@VPW%U?@8phmq{I&a=4b3MT z)MrRdjU;!QPA%4yO+yqLRE~d{9>`q|=kpr|%C&%&ojq#89Agui@Uu-66RZbi5ZOP< zyDze#c+zjyl$nA^p?Gf69gU(*@1HvD*--p=?)?As$#oXr;7u>7 zrT3mZ*t@*Sa_pSxAc}7Le~2p3ztDfg-Hdcm0v7dyA+wO$$qGg7@lB!PQUWV}Va{y5 zq1p3UVCF558|czC;4B~U3}Ht!p2W=@fpbTDP1xOOq*ACw`?TQO<14}`5ZM066NAV< z_eSQ-{y-nWkHKT+kh_6C$uw_(&zF8AyuQ9VmpbBZgn2lWv8NQbt+D|=%4ScjFm#Ff z4+LEqPT7nYdcUBLbU+8%LHBkDb-VQtINflkXIRStE&k0l4FPOy+V!xr6gv1yp;Ot} zAhy$bw*DjB)JHJ?u#MCnAwz%}baLzfA^e5qvfg7K)ael)1eJCHLv5Z7s{(yW1&ylI z$gJ1O106KiKd`C1fF~sv47xlxG=!cjoQ_9^0P<8@YdzxFC|Eaw6421_A#Mxkw#M diff --git a/src/components/layout/AppFooter.jsx b/src/components/layout/AppFooter.jsx index c720c8f5a35b..09087af3a00e 100644 --- a/src/components/layout/AppFooter.jsx +++ b/src/components/layout/AppFooter.jsx @@ -10,7 +10,7 @@ const AppFooter = () => { const isDark = currentTheme === 'impact' || (currentTheme === 'default' && preferredTheme === 'impact') - const datto = isDark ? '/img/datto.png' : '/img/datto.png' + const RoB = isDark ? '/img/RoB.svg' : '/img/RoB-light.svg' const huntress = isDark ? '/img/huntress_teal.png' : '/img/huntress_teal.png' const rewst = isDark ? '/img/rewst_dark.png' : '/img/rewst.png' const ninjaone = isDark ? '/img/ninjaone_dark.png' : '/img/ninjaone.png' @@ -24,8 +24,12 @@ const AppFooter = () => { - - + + From 46b15c72b3b907618fe273d88e6a7188a2ae58fb Mon Sep 17 00:00:00 2001 From: KelvinTegelaar Date: Fri, 30 Aug 2024 18:59:28 +0200 Subject: [PATCH 531/536] update 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 b74d149fe82e..2739d1d2801a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cipp", - "version": "6.3.0", + "version": "6.4.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 798e38995c4d..19b860c1872d 100644 --- a/public/version_latest.txt +++ b/public/version_latest.txt @@ -1 +1 @@ -6.3.0 +6.4.0 diff --git a/version_latest.txt b/version_latest.txt index 798e38995c4d..19b860c1872d 100644 --- a/version_latest.txt +++ b/version_latest.txt @@ -1 +1 @@ -6.3.0 +6.4.0 From 557efcddba65c731f21495873513371abd491bf2 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Thu, 5 Sep 2024 08:42:29 -0400 Subject: [PATCH 532/536] Update .gitignore --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 47dfb11e20c3..0c9f36fe2e8a 100644 --- a/.gitignore +++ b/.gitignore @@ -21,4 +21,5 @@ npm-debug.log* yarn-debug.log* yarn-error.log* -__* \ No newline at end of file +__* +.next/* From b8c26ec862d6cdd9552cd5498c2b5ea6a19fbd4b Mon Sep 17 00:00:00 2001 From: John Duprey Date: Mon, 30 Sep 2024 16:49:12 -0400 Subject: [PATCH 533/536] CippAppPermissionBuilder fixes --- .../utilities/CippAppPermissionBuilder.jsx | 29 +++++++++++++------ 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/src/components/utilities/CippAppPermissionBuilder.jsx b/src/components/utilities/CippAppPermissionBuilder.jsx index d270cfc4a7a7..8959b4f826fa 100644 --- a/src/components/utilities/CippAppPermissionBuilder.jsx +++ b/src/components/utilities/CippAppPermissionBuilder.jsx @@ -60,7 +60,6 @@ const CippAppPermissionBuilder = ({ } = useGenericGetRequestQuery({ path: 'api/ExecServicePrincipals', }) - const [createServicePrincipal, createResult] = useLazyGenericGetRequestQuery() const removeServicePrincipal = (appId) => { @@ -407,9 +406,17 @@ const CippAppPermissionBuilder = ({ ]) const ApiPermissionRow = ({ servicePrincipal = null }) => { + const { + data: servicePrincipalData = [], + isFetching: spFetching, + isSuccess: spIdSuccess, + } = useGenericGetRequestQuery({ + path: 'api/ExecServicePrincipals?Id=' + servicePrincipal.id, + }) + return ( <> - {spSuccess && servicePrincipal !== null && ( + {spSuccess && servicePrincipal !== null && spIdSuccess && ( @@ -457,7 +464,7 @@ const CippAppPermissionBuilder = ({ '.applicationPermissions' } label="Application Permissions" - values={servicePrincipal?.appRoles + values={servicePrincipalData?.Results?.appRoles ?.filter((role) => { return newPermissions?.Permissions[ servicePrincipal.appId @@ -524,8 +531,9 @@ const CippAppPermissionBuilder = ({ }, { selector: (row) => - servicePrincipal.appRoles.find((role) => role.id === row.id) - .description, + servicePrincipalData?.Results?.appRoles.find( + (role) => role.id === row.id, + ).description, name: 'Description', cell: cellGenericFormatter({ wrap: true }), maxWidth: '60%', @@ -577,7 +585,7 @@ const CippAppPermissionBuilder = ({ - {servicePrincipal?.publishedPermissionScopes?.length == 0 && ( + {servicePrincipalData?.Results?.publishedPermissionScopes?.length == 0 && ( No Published Delegated Permissions found. @@ -594,8 +602,8 @@ const CippAppPermissionBuilder = ({ } label="Delegated Permissions" values={ - servicePrincipal?.publishedPermissionScopes?.length > 0 - ? servicePrincipal?.publishedPermissionScopes + servicePrincipalData?.Results?.publishedPermissionScopes?.length > 0 + ? servicePrincipalData?.Results?.publishedPermissionScopes .filter((scopes) => { return newPermissions?.Permissions[ servicePrincipal.appId @@ -664,7 +672,7 @@ const CippAppPermissionBuilder = ({ }, { selector: (row) => - servicePrincipal.publishedPermissionScopes.find( + servicePrincipalData?.Results?.publishedPermissionScopes.find( (scope) => scope?.id === row?.id, )?.userConsentDescription ?? 'No Description', name: 'Description', @@ -921,6 +929,9 @@ const CippAppPermissionBuilder = ({ Object.keys( newPermissions?.MissingPermissions[perm], ).map((type) => { + if (!updatedPermissions.Permissions[perm][type]) { + updatedPermissions.Permissions[perm][type] = [] + } newPermissions?.MissingPermissions[perm][type].map( (p) => { updatedPermissions.Permissions[perm][type].push(p) From 3b87108c37caa7715f47a8f32b9fc296dbf05620 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Fri, 4 Oct 2024 16:55:05 -0400 Subject: [PATCH 534/536] Add zipdeploy --- deployment/AzureDeploymentTemplate.json | 53 +++++++------------ ...AzureDeploymentTemplate_regionoptions.json | 50 +++++++---------- 2 files changed, 36 insertions(+), 67 deletions(-) diff --git a/deployment/AzureDeploymentTemplate.json b/deployment/AzureDeploymentTemplate.json index dc3222498ec6..8bc7b610d9e3 100644 --- a/deployment/AzureDeploymentTemplate.json +++ b/deployment/AzureDeploymentTemplate.json @@ -57,7 +57,7 @@ "objectId": "[reference(resourceId('Microsoft.Web/sites', variables('funcAppName')),'2019-08-01', 'full').identity.principalId]", "permissions": { "keys": [], - "secrets": ["all"], + "secrets": [ "all" ], "certificates": [] } } @@ -116,7 +116,7 @@ ] } ], - "dependsOn": ["[resourceId('Microsoft.Web/sites', variables('funcAppName'))]"] + "dependsOn": [ "[resourceId('Microsoft.Web/sites', variables('funcAppName'))]" ] }, { "apiVersion": "2015-08-01", @@ -135,7 +135,7 @@ "serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('serverFarmName'))]", "siteConfig": { "Use32BitWorkerProcess": false, - "powerShellVersion": "7.2", + "powerShellVersion": "7.4", "appSettings": [ { "name": "AzureWebJobsStorage", @@ -154,20 +154,8 @@ "value": "~4" }, { - "name": "ApplicationID", - "value": "[concat('@Microsoft.KeyVault(SecretUri=https://',variables('uniqueResourceNameBase'), '.vault.azure.net/secrets/ApplicationId)')]" - }, - { - "name": "ApplicationSecret", - "value": "[concat('@Microsoft.KeyVault(SecretUri=https://',variables('uniqueResourceNameBase'), '.vault.azure.net/secrets/ApplicationSecret)')]" - }, - { - "name": "RefreshToken", - "value": "[concat('@Microsoft.KeyVault(SecretUri=https://',variables('uniqueResourceNameBase'), '.vault.azure.net/secrets/RefreshToken)')]" - }, - { - "name": "TenantID", - "value": "[concat('@Microsoft.KeyVault(SecretUri=https://',variables('uniqueResourceNameBase'), '.vault.azure.net/secrets/tenantid)')]" + "name": "WEBSITE_RUN_FROM_PACKAGE", + "value": "1" }, { "name": "FUNCTIONS_WORKER_RUNTIME", @@ -175,24 +163,19 @@ } ] } - }, - "resources": [ - { - "apiVersion": "2015-08-01", - "name": "web", - "type": "sourcecontrols", - "dependsOn": ["[resourceId('Microsoft.Web/sites/', variables('funcAppName'))]"], - "properties": { - "RepoUrl": "[parameters('GithubAPIRepository')]", - "repositoryToken": "[parameters('GithubToken')]", - "token": "[parameters('GithubToken')]", - - "branch": "master", - "publishRunbook": true, - "IsManualIntegration": true - } - } - ] + } + }, + { + "name": "[concat(variables('funcAppName'), '/ZipDeploy')]", + "type": "Microsoft.Web/sites/extensions", + "apiVersion": "2021-02-01", + "location": "[resourceGroup().location]", + "dependsOn": [ + "[resourceId('Microsoft.Web/sites', variables('funcAppName'))]" + ], + "properties": { + "packageUri": "https://cippreleases.blob.core.windows.net/cipp-api/latest.zip" + } }, { "type": "Microsoft.Storage/storageAccounts", diff --git a/deployment/AzureDeploymentTemplate_regionoptions.json b/deployment/AzureDeploymentTemplate_regionoptions.json index ac6691593a0b..ff4d9331da35 100644 --- a/deployment/AzureDeploymentTemplate_regionoptions.json +++ b/deployment/AzureDeploymentTemplate_regionoptions.json @@ -57,7 +57,7 @@ "objectId": "[reference(resourceId('Microsoft.Web/sites', variables('funcAppName')),'2019-08-01', 'full').identity.principalId]", "permissions": { "keys": [], - "secrets": ["all"], + "secrets": [ "all" ], "certificates": [] } } @@ -116,7 +116,7 @@ ] } ], - "dependsOn": ["[resourceId('Microsoft.Web/sites', variables('funcAppName'))]"] + "dependsOn": [ "[resourceId('Microsoft.Web/sites', variables('funcAppName'))]" ] }, { "apiVersion": "2015-08-01", @@ -135,7 +135,7 @@ "serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('serverFarmName'))]", "siteConfig": { "Use32BitWorkerProcess": false, - "powerShellVersion": "7.2", + "powerShellVersion": "7.4", "appSettings": [ { "name": "AzureWebJobsStorage", @@ -154,20 +154,8 @@ "value": "~4" }, { - "name": "ApplicationID", - "value": "[concat('@Microsoft.KeyVault(SecretUri=https://',variables('uniqueResourceNameBase'), '.vault.azure.net/secrets/ApplicationId)')]" - }, - { - "name": "ApplicationSecret", - "value": "[concat('@Microsoft.KeyVault(SecretUri=https://',variables('uniqueResourceNameBase'), '.vault.azure.net/secrets/ApplicationSecret)')]" - }, - { - "name": "RefreshToken", - "value": "[concat('@Microsoft.KeyVault(SecretUri=https://',variables('uniqueResourceNameBase'), '.vault.azure.net/secrets/RefreshToken)')]" - }, - { - "name": "TenantID", - "value": "[concat('@Microsoft.KeyVault(SecretUri=https://',variables('uniqueResourceNameBase'), '.vault.azure.net/secrets/tenantid)')]" + "name": "WEBSITE_RUN_FROM_PACKAGE", + "value": "1" }, { "name": "FUNCTIONS_WORKER_RUNTIME", @@ -175,21 +163,19 @@ } ] } - }, - "resources": [ - { - "apiVersion": "2015-08-01", - "name": "web", - "type": "sourcecontrols", - "dependsOn": ["[resourceId('Microsoft.Web/sites/', variables('funcAppName'))]"], - "properties": { - "RepoUrl": "[parameters('GithubAPIRepository')]", - "branch": "master", - "publishRunbook": true, - "IsManualIntegration": true - } - } - ] + } + }, + { + "name": "[concat(variables('funcAppName'), '/ZipDeploy')]", + "type": "Microsoft.Web/sites/extensions", + "apiVersion": "2021-02-01", + "location": "[resourceGroup().location]", + "dependsOn": [ + "[resourceId('Microsoft.Web/sites', variables('funcAppName'))]" + ], + "properties": { + "packageUri": "https://cippreleases.blob.core.windows.net/cipp-api/latest.zip" + } }, { "type": "Microsoft.Storage/storageAccounts", From 37fb0e6eaff8b652ac2600d37b3a741ca4f61e90 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Fri, 4 Oct 2024 16:58:45 -0400 Subject: [PATCH 535/536] Cleanup api reference --- deployment/AzureDeploymentTemplate.json | 7 ------- deployment/AzureDeploymentTemplate_regionoptions.json | 7 ------- 2 files changed, 14 deletions(-) diff --git a/deployment/AzureDeploymentTemplate.json b/deployment/AzureDeploymentTemplate.json index 8bc7b610d9e3..aa445e3fd6c8 100644 --- a/deployment/AzureDeploymentTemplate.json +++ b/deployment/AzureDeploymentTemplate.json @@ -22,13 +22,6 @@ "metadata": { "description": "Your Github Repository token (see https://docs.microsoft.com/en-us/azure/static-web-apps/publish-azure-resource-manager?tabs=azure-cli#create-a-github-personal-access-token" } - }, - "GithubAPIRepository": { - "defaultValue": "https://github.com/KelvinTegelaar/CIPP-API", - "type": "string", - "metadata": { - "description": "URL to your Github backend fork." - } } }, "variables": { diff --git a/deployment/AzureDeploymentTemplate_regionoptions.json b/deployment/AzureDeploymentTemplate_regionoptions.json index ff4d9331da35..1abff7bcc162 100644 --- a/deployment/AzureDeploymentTemplate_regionoptions.json +++ b/deployment/AzureDeploymentTemplate_regionoptions.json @@ -22,13 +22,6 @@ "metadata": { "description": "Your Github Repository token (see https://docs.microsoft.com/en-us/azure/static-web-apps/publish-azure-resource-manager?tabs=azure-cli#create-a-github-personal-access-token)" } - }, - "GithubAPIRepository": { - "defaultValue": "https://github.com/KelvinTegelaar/CIPP-API", - "type": "string", - "metadata": { - "description": "URL to your Github backend fork." - } } }, "variables": { From f3862a240db2f77359712af2df1057380d282448 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Fri, 4 Oct 2024 19:08:41 -0400 Subject: [PATCH 536/536] 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 2739d1d2801a..1c73c05ffb68 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cipp", - "version": "6.4.0", + "version": "6.4.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 19b860c1872d..4c77920fd2c5 100644 --- a/public/version_latest.txt +++ b/public/version_latest.txt @@ -1 +1 @@ -6.4.0 +6.4.1 diff --git a/version_latest.txt b/version_latest.txt index 19b860c1872d..4c77920fd2c5 100644 --- a/version_latest.txt +++ b/version_latest.txt @@ -1 +1 @@ -6.4.0 +6.4.1