Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "cipp",
"version": "10.4.1",
"version": "10.4.2",
"author": "CIPP Contributors",
"homepage": "https://cipp.app/",
"bugs": {
Expand Down
2 changes: 1 addition & 1 deletion public/version.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
"version": "10.4.1"
"version": "10.4.2"
}
193 changes: 193 additions & 0 deletions src/components/CippComponents/CippReportDBControls.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
import { useState, useEffect, useMemo, useCallback } from 'react'
import { Button, Chip, SvgIcon, Tooltip } from '@mui/material'
import { Stack } from '@mui/system'
import { Sync, CloudDone, Bolt } from '@mui/icons-material'
import { useSettings } from '../../hooks/use-settings'
import { useDialog } from '../../hooks/use-dialog'
import { CippApiDialog } from './CippApiDialog'
import { CippQueueTracker } from '../CippTable/CippQueueTracker'

/**
* Hook + UI component that encapsulates all CIPP Reporting DB cache/live mode logic.
*
* @param {Object} config
* @param {string} config.apiUrl - Base API URL without query params (e.g. "/api/ListMailboxes")
* @param {string} config.queryKey - Base query key (e.g. "ListMailboxes")
* @param {string} config.cacheName - Cache type name for sync (e.g. "Mailboxes", "IntunePolicies")
* @param {string} config.syncTitle - Title for the sync dialog (e.g. "Sync Mailboxes")
* @param {string} [config.syncConfirmText] - Custom confirm text. Default auto-generated from cacheName + tenant.
* @param {Object} [config.syncData] - Extra data to pass to ExecCIPPDBCache. Merged with { Name: cacheName }.
* @param {boolean} [config.allowToggle=true] - Whether the user can toggle between cached and live. False = always cached.
* @param {boolean} [config.defaultCached=true] - Initial cached state (when toggle is allowed).
* @param {string[]} [config.cacheColumns=["CacheTimestamp"]] - Extra columns to show when in cached mode.
* @param {string} [config.tenantColumn="Tenant"] - Column name for tenant (shown in AllTenants mode).
* @param {Object} [config.apiData] - Additional static API data to merge (e.g. extra params).
*
* @returns {Object}
* - useReportDB {boolean} - Current cache mode
* - setUseReportDB {Function} - Manual override (rarely needed)
* - isAllTenants {boolean} - Whether AllTenants is selected
* - resolvedApiUrl {string} - API URL with ?UseReportDB=true appended when needed
* - resolvedApiData {Object|undefined} - Merged apiData (for pages that use apiData instead of URL params)
* - resolvedQueryKey {string} - Query key including tenant and cache mode
* - cacheColumns {string[]} - Columns to prepend/append when cached (includes Tenant for AllTenants)
* - controls {JSX.Element} - Ready-to-render JSX for the cache toggle, sync button, and queue tracker
* - syncDialog {JSX.Element} - The CippApiDialog element to render alongside CippTablePage
*/
export function useCippReportDB(config) {
const {
apiUrl,
queryKey,
cacheName,
syncTitle,
syncConfirmText,
syncData,
allowToggle = true,
defaultCached = true,
cacheColumns = ['CacheTimestamp'],
tenantColumn = 'Tenant',
apiData: extraApiData,
} = config

const currentTenant = useSettings().currentTenant
const isAllTenants = currentTenant === 'AllTenants'
const dialog = useDialog()
const [syncQueueId, setSyncQueueId] = useState(null)
const [useReportDB, setUseReportDB] = useState(defaultCached)

// Reset to default whenever tenant changes; AllTenants always forces cached
useEffect(() => {
if (isAllTenants) {
setUseReportDB(true)
} else {
setUseReportDB(defaultCached)
}
}, [currentTenant, isAllTenants, defaultCached])

// Whether the toggle is actually clickable
const canToggle = allowToggle && !isAllTenants

// Resolved API URL — append UseReportDB param when cached
const resolvedApiUrl = useMemo(() => {
if (!useReportDB) return apiUrl
const sep = apiUrl.includes('?') ? '&' : '?'
return `${apiUrl}${sep}UseReportDB=true`
}, [apiUrl, useReportDB])

// Keep mode flag in the URL only; CippTablePage merges apiData into query params.
const resolvedApiData = useMemo(() => {
if (!extraApiData) return undefined
return {
...extraApiData,
}
}, [extraApiData])

// Query key that includes tenant + mode for proper cache separation
const resolvedQueryKey = useMemo(() => {
return `${queryKey}-${currentTenant}-${useReportDB}`
}, [queryKey, currentTenant, useReportDB])

// Extra columns to show when in cached mode
const extraColumns = useMemo(() => {
const cols = []
if (useReportDB && isAllTenants) {
cols.push(tenantColumn)
}
if (useReportDB) {
cols.push(...cacheColumns)
}
return cols
}, [useReportDB, isAllTenants, tenantColumn, cacheColumns])

const handleSyncSuccess = useCallback((result) => {
if (result?.Metadata?.QueueId) {
setSyncQueueId(result.Metadata.QueueId)
}
}, [])

// Tooltip text
const tooltipText = !allowToggle
? 'This page always uses cached data from the CIPP reporting database.'
: isAllTenants
? 'AllTenants always uses cached data'
: useReportDB
? 'Showing cached data — click to switch to live'
: 'Showing live data — click to switch to cache'

const confirmText =
syncConfirmText ||
`Run ${cacheName} cache sync for ${currentTenant}? This will update data immediately.`

// The controls JSX
const controls = (
<Stack direction="row" spacing={1} alignItems="center">
{useReportDB && (
<>
<CippQueueTracker
queueId={syncQueueId}
queryKey={resolvedQueryKey}
title={syncTitle}
/>
<Button
startIcon={
<SvgIcon fontSize="small">
<Sync />
</SvgIcon>
}
size="xs"
onClick={dialog.handleOpen}
disabled={isAllTenants}
>
Sync
</Button>
</>
)}
<Tooltip title={tooltipText}>
<span>
<Chip
icon={useReportDB ? <CloudDone /> : <Bolt />}
label={useReportDB ? 'Cached' : 'Live'}
color="primary"
size="small"
onClick={canToggle ? () => setUseReportDB((prev) => !prev) : undefined}
clickable={canToggle}
disabled={!canToggle}
variant="outlined"
/>
</span>
</Tooltip>
</Stack>
)

// The sync dialog JSX — render alongside the table page
const syncDialogElement = (
<CippApiDialog
createDialog={dialog}
title={syncTitle}
fields={[]}
api={{
type: 'GET',
url: '/api/ExecCIPPDBCache',
confirmText,
relatedQueryKeys: [`${queryKey}-${currentTenant}-true`],
data: {
Name: cacheName,
...(syncData || {}),
},
onSuccess: handleSyncSuccess,
}}
/>
)

return {
useReportDB,
setUseReportDB,
isAllTenants,
resolvedApiUrl,
resolvedApiData,
resolvedQueryKey,
cacheColumns: extraColumns,
controls,
syncDialog: syncDialogElement,
}
}
11 changes: 11 additions & 0 deletions src/components/CippFormPages/CippAddEditUser.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -851,6 +851,17 @@ const CippAddEditUser = (props) => {
}))}
creatable={false}
formControl={formControl}
customAction={{
icon: <Sync />,
tooltip: 'Refresh groups',
onClick: () => {
tenantGroups.refetch()
if (formType === 'edit') {
userGroups.refetch()
}
},
position: 'outside',
}}
/>
</Grid>
)}
Expand Down
15 changes: 15 additions & 0 deletions src/data/CIPPDBCacheTypes.json
Original file line number Diff line number Diff line change
Expand Up @@ -254,11 +254,26 @@
"friendlyName": "Mailbox Usage",
"description": "Exchange Online mailbox usage statistics"
},
{
"type": "OneDriveSiteListing",
"friendlyName": "OneDrive Site Listing",
"description": "OneDrive personal site listing details used for usage reporting"
},
{
"type": "OneDriveUsage",
"friendlyName": "OneDrive Usage",
"description": "OneDrive usage statistics"
},
{
"type": "SharePointSiteListing",
"friendlyName": "SharePoint Site Listing",
"description": "SharePoint site listing details used for usage reporting"
},
{
"type": "SharePointSiteUsage",
"friendlyName": "SharePoint Site Usage",
"description": "SharePoint site usage statistics"
},
{
"type": "ConditionalAccessPolicies",
"friendlyName": "Conditional Access Policies",
Expand Down
29 changes: 22 additions & 7 deletions src/data/cipp-roles.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@
{
"readonly": {
"include": ["*.Read"],
"exclude": ["CIPP.SuperAdmin.*"]
"include": [
"*.Read"
],
"exclude": [
"CIPP.SuperAdmin.*",
"CIPP.Admin.*",
"CIPP.AppSettings.*"
]
},
"editor": {
"include": ["*.Read", "*.ReadWrite"],
"include": [
"*.Read",
"*.ReadWrite"
],
"exclude": [
"CIPP.SuperAdmin.*",
"CIPP.Admin.*",
Expand All @@ -13,11 +22,17 @@
]
},
"admin": {
"include": ["*"],
"exclude": ["CIPP.SuperAdmin.*"]
"include": [
"*"
],
"exclude": [
"CIPP.SuperAdmin.*"
]
},
"superadmin": {
"include": ["*"],
"include": [
"*"
],
"exclude": []
}
}
}
68 changes: 68 additions & 0 deletions src/data/standards.json
Original file line number Diff line number Diff line change
Expand Up @@ -6304,5 +6304,73 @@
"EXCHANGE_S_ENTERPRISE_GOV",
"EXCHANGE_LITE"
]
},
{
"name": "standards.SPDisableCustomScripts",
"cat": "SharePoint Standards",
"tag": [],
"helpText": "Prevents users from running custom scripts on SharePoint and OneDrive sites. Custom scripts can modify site behaviors and bypass governance controls.",
"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.",
"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.",
"addedComponent": [],
"label": "Disable custom scripts on SharePoint sites",
"impact": "High Impact",
"impactColour": "danger",
"addedDate": "2026-04-28",
"powershellEquivalent": "Set-SPOTenant -CustomScriptsRestrictMode $true",
"recommendedBy": ["CIPP"],
"requiredCapabilities": [
"SHAREPOINTWAC",
"SHAREPOINTSTANDARD",
"SHAREPOINTENTERPRISE",
"SHAREPOINTENTERPRISE_EDU",
"ONEDRIVE_BASIC",
"ONEDRIVE_ENTERPRISE"
]
},
{
"name": "standards.SPDisableStoreAccess",
"cat": "SharePoint Standards",
"tag": [],
"helpText": "Disables end users from installing applications from the Microsoft Store into SharePoint sites.",
"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.",
"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.",
"addedComponent": [],
"label": "Disable SharePoint Store access",
"impact": "Low Impact",
"impactColour": "info",
"addedDate": "2026-04-28",
"powershellEquivalent": "Set-SPOTenant -DisableSharePointStoreAccess $true",
"recommendedBy": ["CIPP"],
"requiredCapabilities": [
"SHAREPOINTWAC",
"SHAREPOINTSTANDARD",
"SHAREPOINTENTERPRISE",
"SHAREPOINTENTERPRISE_EDU",
"ONEDRIVE_BASIC",
"ONEDRIVE_ENTERPRISE"
]
},
{
"name": "standards.DisableEWS",
"cat": "Exchange Standards",
"tag": [],
"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.",
"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.",
"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.",
"addedComponent": [],
"label": "Disable Exchange Web Services",
"impact": "High Impact",
"impactColour": "danger",
"addedDate": "2026-04-28",
"powershellEquivalent": "Set-OrganizationConfig -EwsEnabled $false",
"recommendedBy": ["CIPP"],
"requiredCapabilities": [
"EXCHANGE_S_STANDARD",
"EXCHANGE_S_ENTERPRISE",
"EXCHANGE_S_STANDARD_GOV",
"EXCHANGE_S_ENTERPRISE_GOV",
"EXCHANGE_LITE"
]
}
]
Loading