Skip to content

Commit 8b0cd1a

Browse files
authored
Merge pull request KelvinTegelaar#5937 from KelvinTegelaar/dev
Dev to hotfix
2 parents e4f978a + fbe9bc2 commit 8b0cd1a

25 files changed

Lines changed: 991 additions & 1205 deletions

File tree

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "cipp",
3-
"version": "10.4.1",
3+
"version": "10.4.2",
44
"author": "CIPP Contributors",
55
"homepage": "https://cipp.app/",
66
"bugs": {

public/version.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
{
2-
"version": "10.4.1"
2+
"version": "10.4.2"
33
}
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
import { useState, useEffect, useMemo, useCallback } from 'react'
2+
import { Button, Chip, SvgIcon, Tooltip } from '@mui/material'
3+
import { Stack } from '@mui/system'
4+
import { Sync, CloudDone, Bolt } from '@mui/icons-material'
5+
import { useSettings } from '../../hooks/use-settings'
6+
import { useDialog } from '../../hooks/use-dialog'
7+
import { CippApiDialog } from './CippApiDialog'
8+
import { CippQueueTracker } from '../CippTable/CippQueueTracker'
9+
10+
/**
11+
* Hook + UI component that encapsulates all CIPP Reporting DB cache/live mode logic.
12+
*
13+
* @param {Object} config
14+
* @param {string} config.apiUrl - Base API URL without query params (e.g. "/api/ListMailboxes")
15+
* @param {string} config.queryKey - Base query key (e.g. "ListMailboxes")
16+
* @param {string} config.cacheName - Cache type name for sync (e.g. "Mailboxes", "IntunePolicies")
17+
* @param {string} config.syncTitle - Title for the sync dialog (e.g. "Sync Mailboxes")
18+
* @param {string} [config.syncConfirmText] - Custom confirm text. Default auto-generated from cacheName + tenant.
19+
* @param {Object} [config.syncData] - Extra data to pass to ExecCIPPDBCache. Merged with { Name: cacheName }.
20+
* @param {boolean} [config.allowToggle=true] - Whether the user can toggle between cached and live. False = always cached.
21+
* @param {boolean} [config.defaultCached=true] - Initial cached state (when toggle is allowed).
22+
* @param {string[]} [config.cacheColumns=["CacheTimestamp"]] - Extra columns to show when in cached mode.
23+
* @param {string} [config.tenantColumn="Tenant"] - Column name for tenant (shown in AllTenants mode).
24+
* @param {Object} [config.apiData] - Additional static API data to merge (e.g. extra params).
25+
*
26+
* @returns {Object}
27+
* - useReportDB {boolean} - Current cache mode
28+
* - setUseReportDB {Function} - Manual override (rarely needed)
29+
* - isAllTenants {boolean} - Whether AllTenants is selected
30+
* - resolvedApiUrl {string} - API URL with ?UseReportDB=true appended when needed
31+
* - resolvedApiData {Object|undefined} - Merged apiData (for pages that use apiData instead of URL params)
32+
* - resolvedQueryKey {string} - Query key including tenant and cache mode
33+
* - cacheColumns {string[]} - Columns to prepend/append when cached (includes Tenant for AllTenants)
34+
* - controls {JSX.Element} - Ready-to-render JSX for the cache toggle, sync button, and queue tracker
35+
* - syncDialog {JSX.Element} - The CippApiDialog element to render alongside CippTablePage
36+
*/
37+
export function useCippReportDB(config) {
38+
const {
39+
apiUrl,
40+
queryKey,
41+
cacheName,
42+
syncTitle,
43+
syncConfirmText,
44+
syncData,
45+
allowToggle = true,
46+
defaultCached = true,
47+
cacheColumns = ['CacheTimestamp'],
48+
tenantColumn = 'Tenant',
49+
apiData: extraApiData,
50+
} = config
51+
52+
const currentTenant = useSettings().currentTenant
53+
const isAllTenants = currentTenant === 'AllTenants'
54+
const dialog = useDialog()
55+
const [syncQueueId, setSyncQueueId] = useState(null)
56+
const [useReportDB, setUseReportDB] = useState(defaultCached)
57+
58+
// Reset to default whenever tenant changes; AllTenants always forces cached
59+
useEffect(() => {
60+
if (isAllTenants) {
61+
setUseReportDB(true)
62+
} else {
63+
setUseReportDB(defaultCached)
64+
}
65+
}, [currentTenant, isAllTenants, defaultCached])
66+
67+
// Whether the toggle is actually clickable
68+
const canToggle = allowToggle && !isAllTenants
69+
70+
// Resolved API URL — append UseReportDB param when cached
71+
const resolvedApiUrl = useMemo(() => {
72+
if (!useReportDB) return apiUrl
73+
const sep = apiUrl.includes('?') ? '&' : '?'
74+
return `${apiUrl}${sep}UseReportDB=true`
75+
}, [apiUrl, useReportDB])
76+
77+
// Keep mode flag in the URL only; CippTablePage merges apiData into query params.
78+
const resolvedApiData = useMemo(() => {
79+
if (!extraApiData) return undefined
80+
return {
81+
...extraApiData,
82+
}
83+
}, [extraApiData])
84+
85+
// Query key that includes tenant + mode for proper cache separation
86+
const resolvedQueryKey = useMemo(() => {
87+
return `${queryKey}-${currentTenant}-${useReportDB}`
88+
}, [queryKey, currentTenant, useReportDB])
89+
90+
// Extra columns to show when in cached mode
91+
const extraColumns = useMemo(() => {
92+
const cols = []
93+
if (useReportDB && isAllTenants) {
94+
cols.push(tenantColumn)
95+
}
96+
if (useReportDB) {
97+
cols.push(...cacheColumns)
98+
}
99+
return cols
100+
}, [useReportDB, isAllTenants, tenantColumn, cacheColumns])
101+
102+
const handleSyncSuccess = useCallback((result) => {
103+
if (result?.Metadata?.QueueId) {
104+
setSyncQueueId(result.Metadata.QueueId)
105+
}
106+
}, [])
107+
108+
// Tooltip text
109+
const tooltipText = !allowToggle
110+
? 'This page always uses cached data from the CIPP reporting database.'
111+
: isAllTenants
112+
? 'AllTenants always uses cached data'
113+
: useReportDB
114+
? 'Showing cached data — click to switch to live'
115+
: 'Showing live data — click to switch to cache'
116+
117+
const confirmText =
118+
syncConfirmText ||
119+
`Run ${cacheName} cache sync for ${currentTenant}? This will update data immediately.`
120+
121+
// The controls JSX
122+
const controls = (
123+
<Stack direction="row" spacing={1} alignItems="center">
124+
{useReportDB && (
125+
<>
126+
<CippQueueTracker
127+
queueId={syncQueueId}
128+
queryKey={resolvedQueryKey}
129+
title={syncTitle}
130+
/>
131+
<Button
132+
startIcon={
133+
<SvgIcon fontSize="small">
134+
<Sync />
135+
</SvgIcon>
136+
}
137+
size="xs"
138+
onClick={dialog.handleOpen}
139+
disabled={isAllTenants}
140+
>
141+
Sync
142+
</Button>
143+
</>
144+
)}
145+
<Tooltip title={tooltipText}>
146+
<span>
147+
<Chip
148+
icon={useReportDB ? <CloudDone /> : <Bolt />}
149+
label={useReportDB ? 'Cached' : 'Live'}
150+
color="primary"
151+
size="small"
152+
onClick={canToggle ? () => setUseReportDB((prev) => !prev) : undefined}
153+
clickable={canToggle}
154+
disabled={!canToggle}
155+
variant="outlined"
156+
/>
157+
</span>
158+
</Tooltip>
159+
</Stack>
160+
)
161+
162+
// The sync dialog JSX — render alongside the table page
163+
const syncDialogElement = (
164+
<CippApiDialog
165+
createDialog={dialog}
166+
title={syncTitle}
167+
fields={[]}
168+
api={{
169+
type: 'GET',
170+
url: '/api/ExecCIPPDBCache',
171+
confirmText,
172+
relatedQueryKeys: [`${queryKey}-${currentTenant}-true`],
173+
data: {
174+
Name: cacheName,
175+
...(syncData || {}),
176+
},
177+
onSuccess: handleSyncSuccess,
178+
}}
179+
/>
180+
)
181+
182+
return {
183+
useReportDB,
184+
setUseReportDB,
185+
isAllTenants,
186+
resolvedApiUrl,
187+
resolvedApiData,
188+
resolvedQueryKey,
189+
cacheColumns: extraColumns,
190+
controls,
191+
syncDialog: syncDialogElement,
192+
}
193+
}

src/components/CippFormPages/CippAddEditUser.jsx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -851,6 +851,17 @@ const CippAddEditUser = (props) => {
851851
}))}
852852
creatable={false}
853853
formControl={formControl}
854+
customAction={{
855+
icon: <Sync />,
856+
tooltip: 'Refresh groups',
857+
onClick: () => {
858+
tenantGroups.refetch()
859+
if (formType === 'edit') {
860+
userGroups.refetch()
861+
}
862+
},
863+
position: 'outside',
864+
}}
854865
/>
855866
</Grid>
856867
)}

src/data/CIPPDBCacheTypes.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,11 +254,26 @@
254254
"friendlyName": "Mailbox Usage",
255255
"description": "Exchange Online mailbox usage statistics"
256256
},
257+
{
258+
"type": "OneDriveSiteListing",
259+
"friendlyName": "OneDrive Site Listing",
260+
"description": "OneDrive personal site listing details used for usage reporting"
261+
},
257262
{
258263
"type": "OneDriveUsage",
259264
"friendlyName": "OneDrive Usage",
260265
"description": "OneDrive usage statistics"
261266
},
267+
{
268+
"type": "SharePointSiteListing",
269+
"friendlyName": "SharePoint Site Listing",
270+
"description": "SharePoint site listing details used for usage reporting"
271+
},
272+
{
273+
"type": "SharePointSiteUsage",
274+
"friendlyName": "SharePoint Site Usage",
275+
"description": "SharePoint site usage statistics"
276+
},
262277
{
263278
"type": "ConditionalAccessPolicies",
264279
"friendlyName": "Conditional Access Policies",

src/data/cipp-roles.json

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,19 @@
11
{
22
"readonly": {
3-
"include": ["*.Read"],
4-
"exclude": ["CIPP.SuperAdmin.*"]
3+
"include": [
4+
"*.Read"
5+
],
6+
"exclude": [
7+
"CIPP.SuperAdmin.*",
8+
"CIPP.Admin.*",
9+
"CIPP.AppSettings.*"
10+
]
511
},
612
"editor": {
7-
"include": ["*.Read", "*.ReadWrite"],
13+
"include": [
14+
"*.Read",
15+
"*.ReadWrite"
16+
],
817
"exclude": [
918
"CIPP.SuperAdmin.*",
1019
"CIPP.Admin.*",
@@ -13,11 +22,17 @@
1322
]
1423
},
1524
"admin": {
16-
"include": ["*"],
17-
"exclude": ["CIPP.SuperAdmin.*"]
25+
"include": [
26+
"*"
27+
],
28+
"exclude": [
29+
"CIPP.SuperAdmin.*"
30+
]
1831
},
1932
"superadmin": {
20-
"include": ["*"],
33+
"include": [
34+
"*"
35+
],
2136
"exclude": []
2237
}
23-
}
38+
}

src/data/standards.json

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6304,5 +6304,73 @@
63046304
"EXCHANGE_S_ENTERPRISE_GOV",
63056305
"EXCHANGE_LITE"
63066306
]
6307+
},
6308+
{
6309+
"name": "standards.SPDisableCustomScripts",
6310+
"cat": "SharePoint Standards",
6311+
"tag": [],
6312+
"helpText": "Prevents users from running custom scripts on SharePoint and OneDrive sites. Custom scripts can modify site behaviors and bypass governance controls.",
6313+
"docsDescription": "Disables the ability to add and run custom scripts on SharePoint and OneDrive sites at the tenant level. When custom scripts are allowed, governance cannot be enforced, and the capabilities of inserted code cannot be scoped or blocked. Microsoft recommends using the SharePoint Framework instead of custom scripts.",
6314+
"executiveText": "Blocks custom scripts from being added to SharePoint and OneDrive sites, enforcing governance controls and preventing unscoped code execution. This aligns with Microsoft's Baseline Security Mode recommendation to permanently remove the ability to add new custom scripts, directing organizations to use the SharePoint Framework instead.",
6315+
"addedComponent": [],
6316+
"label": "Disable custom scripts on SharePoint sites",
6317+
"impact": "High Impact",
6318+
"impactColour": "danger",
6319+
"addedDate": "2026-04-28",
6320+
"powershellEquivalent": "Set-SPOTenant -CustomScriptsRestrictMode $true",
6321+
"recommendedBy": ["CIPP"],
6322+
"requiredCapabilities": [
6323+
"SHAREPOINTWAC",
6324+
"SHAREPOINTSTANDARD",
6325+
"SHAREPOINTENTERPRISE",
6326+
"SHAREPOINTENTERPRISE_EDU",
6327+
"ONEDRIVE_BASIC",
6328+
"ONEDRIVE_ENTERPRISE"
6329+
]
6330+
},
6331+
{
6332+
"name": "standards.SPDisableStoreAccess",
6333+
"cat": "SharePoint Standards",
6334+
"tag": [],
6335+
"helpText": "Disables end users from installing applications from the Microsoft Store into SharePoint sites.",
6336+
"docsDescription": "Removes the ability for end users to install applications directly from the Microsoft Store into SharePoint. This prevents uncontrolled app installations that can increase governance costs and go against organizational policies.",
6337+
"executiveText": "Prevents end users from installing applications from the Microsoft Store into SharePoint sites, ensuring that only approved applications are available. This reduces governance overhead and aligns with Microsoft's Baseline Security Mode recommendations.",
6338+
"addedComponent": [],
6339+
"label": "Disable SharePoint Store access",
6340+
"impact": "Low Impact",
6341+
"impactColour": "info",
6342+
"addedDate": "2026-04-28",
6343+
"powershellEquivalent": "Set-SPOTenant -DisableSharePointStoreAccess $true",
6344+
"recommendedBy": ["CIPP"],
6345+
"requiredCapabilities": [
6346+
"SHAREPOINTWAC",
6347+
"SHAREPOINTSTANDARD",
6348+
"SHAREPOINTENTERPRISE",
6349+
"SHAREPOINTENTERPRISE_EDU",
6350+
"ONEDRIVE_BASIC",
6351+
"ONEDRIVE_ENTERPRISE"
6352+
]
6353+
},
6354+
{
6355+
"name": "standards.DisableEWS",
6356+
"cat": "Exchange Standards",
6357+
"tag": [],
6358+
"helpText": "Disables Exchange Web Services (EWS) organization-wide. This reduces the attack surface by blocking legacy API access to mailbox data. Warning: This may break Office web add-ins on builds older than 16.0.19127.",
6359+
"docsDescription": "Disables Exchange Web Services (EWS) at the organization level to reduce attack surface. EWS provides cross-platform API access to sensitive Exchange Online data such as emails, meetings, and contacts. If compromised, attackers can access confidential data, send phishing emails, or spoof identities. Disabling EWS also reduces legacy app usage and minimizes exploitable endpoints. Note that this may break first-party features including web add-ins for Word, Excel, PowerPoint, and Outlook on builds older than 16.0.19127.",
6360+
"executiveText": "Disables Exchange Web Services (EWS) across the organization to reduce attack surface and prevent legacy API access to sensitive mailbox data. This aligns with Microsoft's Baseline Security Mode recommendation to minimize exploitable endpoints while requiring updates to applications that depend on EWS.",
6361+
"addedComponent": [],
6362+
"label": "Disable Exchange Web Services",
6363+
"impact": "High Impact",
6364+
"impactColour": "danger",
6365+
"addedDate": "2026-04-28",
6366+
"powershellEquivalent": "Set-OrganizationConfig -EwsEnabled $false",
6367+
"recommendedBy": ["CIPP"],
6368+
"requiredCapabilities": [
6369+
"EXCHANGE_S_STANDARD",
6370+
"EXCHANGE_S_ENTERPRISE",
6371+
"EXCHANGE_S_STANDARD_GOV",
6372+
"EXCHANGE_S_ENTERPRISE_GOV",
6373+
"EXCHANGE_LITE"
6374+
]
63076375
}
63086376
]

0 commit comments

Comments
 (0)