Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
4d09517
feat(mem): use tooltip-enabled policy details in compare view
kris6673 Apr 30, 2026
17077aa
feat(compare): add null safety
kris6673 Apr 30, 2026
1bcf7bd
feat(intune): show administrative template policy details
kris6673 Apr 30, 2026
43bec67
tag and standard updates
KelvinTegelaar May 7, 2026
d8aa246
TAP audit log prebuilt alert
Zacgoose May 7, 2026
d3c75d8
more test suite tags
KelvinTegelaar May 7, 2026
d53745f
Merge branch 'dev' of https://github.com/KelvinTegelaar/CIPP into dev
KelvinTegelaar May 7, 2026
b898428
Merge pull request #5951 from kris6673/admin-templates
KelvinTegelaar May 7, 2026
397d0c9
add purview section
KelvinTegelaar May 7, 2026
1b51746
feat: add AutoDiscover data retrieval to CippDomainCards
kris6673 May 7, 2026
038c9f6
Add Investigate status to custom tests
Zacgoose May 8, 2026
b0661ac
Update AuditLogTemplates.json
Zacgoose May 8, 2026
f2d78d1
Merge pull request #5974 from kris6673/issue5972
KelvinTegelaar May 8, 2026
922be32
Merge pull request #5971 from joaadvi/feat/jit-admin-usage-location
KelvinTegelaar May 8, 2026
406b2d5
Merge pull request #5950 from kris6673/feat/compare-intune-policy-too…
KelvinTegelaar May 8, 2026
182f0c8
pushing new compliance menus
KelvinTegelaar May 8, 2026
78fa718
Merge branch 'dev' of https://github.com/KelvinTegelaar/CIPP into dev
KelvinTegelaar May 8, 2026
2a59c79
feat: add manager and sponsor properties to user patching
kris6673 May 8, 2026
ad8ca14
Merge pull request #5976 from kris6673/5933
KelvinTegelaar May 8, 2026
d251423
fix(jit-admin): submit TAP lifetime within policy bounds
kris6673 May 8, 2026
6e1466a
Merge pull request #5977 from kris6673/5965
KelvinTegelaar May 8, 2026
ed9d0f9
Disable all tenant support for message trace
Zacgoose May 8, 2026
8e09f22
add make to portals list
rvdwegen May 8, 2026
3f7ed1f
feat: add Indirect Reseller Link component and integrate into onboard…
JohnDuprey May 8, 2026
3878e5c
fix typo
JohnDuprey May 8, 2026
f482fa8
fix: minor tweaks
JohnDuprey May 8, 2026
64a5439
chore: bump version to 10.4.4
JohnDuprey May 8, 2026
ff61271
Merge pull request #5985 from KelvinTegelaar/dev
JohnDuprey May 8, 2026
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.3",
"version": "10.4.4",
"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.3"
"version": "10.4.4"
}
27 changes: 27 additions & 0 deletions src/components/CippCards/CippDomainCards.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -470,6 +470,13 @@ export const CippDomainCards = ({ domain: propDomain = "", fullwidth = false })
waiting: !!domain,
});

const { data: autoDiscoverData, isFetching: autoDiscoverLoading } = ApiGetCall({
url: "/api/ListDomainHealth",
queryKey: `autodiscover-${domain}`,
data: { Domain: domain, Action: "ReadAutoDiscover" },
waiting: !!domain,
});

const { data: httpsData, isFetching: httpsLoading } = ApiGetCall({
url: "/api/ListDomainHealth",
queryKey: `https-${domain}-${subdomains}`,
Expand Down Expand Up @@ -684,6 +691,26 @@ export const CippDomainCards = ({ domain: propDomain = "", fullwidth = false })
}
/>
</Grid>
<Grid size={{ md: gridItemSize, xs: 12 }}>
<DomainResultCard
title="AutoDiscover"
data={autoDiscoverData}
isFetching={autoDiscoverLoading}
info={
<div>
<p>
AutoDiscover ({autoDiscoverData?.RecordType || "None"}):
</p>
<CippCodeBlock code={autoDiscoverData?.Record || "No record found"} />
<ResultList
passes={autoDiscoverData?.ValidationPasses}
warns={autoDiscoverData?.ValidationWarns}
fails={autoDiscoverData?.ValidationFails}
/>
</div>
}
/>
</Grid>
{enableHttps && (
<Grid size={{ md: gridItemSize, xs: 12 }}>
<DomainResultCard
Expand Down
259 changes: 259 additions & 0 deletions src/components/CippComponents/CippDeployCompliancePolicyDrawer.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,259 @@
import React, { useEffect, useState } from "react";
import { Button, Divider, Stack } from "@mui/material";
import { Grid } from "@mui/system";
import { useForm, useFormState, useWatch } from "react-hook-form";
import { RocketLaunch } from "@mui/icons-material";
import { CippOffCanvas } from "./CippOffCanvas";
import CippFormComponent from "./CippFormComponent";
import { CippFormTenantSelector } from "./CippFormTenantSelector";
import { CippApiResults } from "./CippApiResults";
import { ApiPostCall } from "../../api/ApiCall";

const MODE_CONFIG = {
DlpCompliancePolicy: {
title: "Deploy DLP Compliance Policy",
buttonLabel: "Deploy DLP Policy",
postUrl: "/api/AddDlpCompliancePolicy",
listTemplatesUrl: "/api/ListDlpCompliancePolicyTemplates",
templateQueryKey: "TemplateListDlpCompliancePolicy",
relatedQueryKeys: ["ListDlpCompliancePolicy", "ListDlpCompliancePolicyTemplates"],
placeholder: `{
"Name": "Block Credit Card data",
"Comment": "Blocks documents containing credit card numbers",
"Mode": "Enable",
"ExchangeLocation": "All",
"SharePointLocation": "All",
"OneDriveLocation": "All",
"RuleParams": {
"Name": "Block Credit Card data Rule",
"ContentContainsSensitiveInformation": [{ "name": "Credit Card Number", "minCount": "1" }],
"BlockAccess": true
}
}`,
},
RetentionCompliancePolicy: {
title: "Deploy Retention Compliance Policy",
buttonLabel: "Deploy Retention Policy",
postUrl: "/api/AddRetentionCompliancePolicy",
listTemplatesUrl: "/api/ListRetentionCompliancePolicyTemplates",
templateQueryKey: "TemplateListRetentionCompliancePolicy",
relatedQueryKeys: [
"ListRetentionCompliancePolicy",
"ListRetentionCompliancePolicyTemplates",
],
placeholder: `{
"Name": "7-year Email Retention",
"Comment": "Retain Exchange mail for 7 years",
"ExchangeLocation": "All",
"Enabled": true,
"RuleParams": {
"Name": "7-year Email Retention Rule",
"RetentionDuration": 2555,
"RetentionComplianceAction": "Keep",
"ExpirationDateOption": "ModificationAgeInDays"
}
}`,
},
SensitivityLabel: {
title: "Deploy Sensitivity Label",
buttonLabel: "Deploy Sensitivity Label",
postUrl: "/api/AddSensitivityLabel",
listTemplatesUrl: "/api/ListSensitivityLabelTemplates",
templateQueryKey: "TemplateListSensitivityLabel",
relatedQueryKeys: ["ListSensitivityLabel", "ListSensitivityLabelTemplates"],
placeholder: `{
"Name": "Confidential",
"DisplayName": "Confidential",
"Tooltip": "Confidential data, do not share externally",
"Comment": "Internal-only confidential classification",
"ContentType": "File, Email",
"EncryptionEnabled": true,
"EncryptionProtectionType": "Template",
"ContentMarkingHeaderEnabled": true,
"ContentMarkingHeaderText": "Confidential - Internal Use Only",
"PolicyParams": {
"Name": "Confidential Label Policy",
"ExchangeLocation": "All",
"Settings": [
["mandatory", "false"],
["disablemandatoryinoutlook", "true"]
]
}
}`,
},
SensitiveInfoType: {
title: "Deploy Sensitive Information Type",
buttonLabel: "Deploy SIT",
postUrl: "/api/AddSensitiveInfoType",
listTemplatesUrl: "/api/ListSensitiveInfoTypeTemplates",
templateQueryKey: "TemplateListSensitiveInfoType",
relatedQueryKeys: ["ListSensitiveInfoType", "ListSensitiveInfoTypeTemplates"],
placeholder: `{
"Name": "Custom Employee ID",
"Description": "Internal Employee ID format EMP-NNNNN",
"Pattern": "EMP-\\\\d{5}",
"Confidence": "High",
"Recommended": true
}

// Or with a base64-encoded XML rule pack:
// {
// "Name": "Custom Rule Pack",
// "FileDataBase64": "<BASE64 encoded XML rule pack>"
// }`,
},
};

export const CippDeployCompliancePolicyDrawer = ({
mode,
buttonText,
requiredPermissions = [],
PermissionButton = Button,
}) => {
const config = MODE_CONFIG[mode];

const [drawerVisible, setDrawerVisible] = useState(false);

const formControl = useForm({
mode: "onChange",
defaultValues: {
selectedTenants: [],
TemplateList: null,
PowerShellCommand: "",
},
});

const { isValid } = useFormState({ control: formControl.control });

const templateListVal = useWatch({ control: formControl.control, name: "TemplateList" });

useEffect(() => {
if (templateListVal?.value) {
formControl.setValue("PowerShellCommand", JSON.stringify(templateListVal.value));
}
}, [templateListVal, formControl]);

const deployPolicy = ApiPostCall({
urlFromData: true,
relatedQueryKeys: config?.relatedQueryKeys ?? [],
});

useEffect(() => {
if (deployPolicy.isSuccess) {
formControl.reset({
selectedTenants: [],
TemplateList: null,
PowerShellCommand: "",
});
}
}, [deployPolicy.isSuccess, formControl]);

if (!config) {
return null;
}

const handleSubmit = () => {
formControl.trigger();
if (!isValid) return;
deployPolicy.mutate({
url: config.postUrl,
data: formControl.getValues(),
});
};

const handleCloseDrawer = () => {
setDrawerVisible(false);
formControl.reset({
selectedTenants: [],
TemplateList: null,
PowerShellCommand: "",
});
};

return (
<>
<PermissionButton
requiredPermissions={requiredPermissions}
onClick={() => setDrawerVisible(true)}
startIcon={<RocketLaunch />}
>
{buttonText ?? config.buttonLabel}
</PermissionButton>
<CippOffCanvas
title={config.title}
visible={drawerVisible}
onClose={handleCloseDrawer}
size="lg"
footer={
<Stack direction="row" spacing={2} justifyContent="flex-start">
<Button
variant="contained"
color="primary"
onClick={handleSubmit}
disabled={deployPolicy.isLoading || !isValid}
>
{deployPolicy.isLoading
? "Deploying..."
: deployPolicy.isSuccess
? "Deploy Another"
: "Deploy"}
</Button>
<Button variant="outlined" onClick={handleCloseDrawer}>
Close
</Button>
</Stack>
}
>
<Grid container spacing={2} sx={{ mb: 2 }}>
<Grid size={{ xs: 12 }}>
<CippFormTenantSelector
label="Select Tenants"
formControl={formControl}
name="selectedTenants"
type="multiple"
allTenants={true}
preselectedEnabled={true}
validators={{ required: "At least one tenant must be selected" }}
/>
</Grid>

<Divider sx={{ my: 1, width: "100%" }} />

<Grid size={{ xs: 12 }}>
<CippFormComponent
type="autoComplete"
label="Select a template (optional)"
name="TemplateList"
formControl={formControl}
multiple={false}
api={{
queryKey: config.templateQueryKey,
labelField: "name",
valueField: (option) => option,
url: config.listTemplatesUrl,
}}
placeholder="Select a template or enter PowerShell JSON manually"
/>
</Grid>

<Divider sx={{ my: 1, width: "100%" }} />

<Grid size={{ xs: 12 }}>
<CippFormComponent
type="textField"
label="Parameters (JSON)"
name="PowerShellCommand"
formControl={formControl}
multiline
rows={12}
validators={{ required: "Please enter the PowerShell parameters as JSON." }}
placeholder={config.placeholder}
/>
</Grid>

<CippApiResults apiObject={deployPolicy} />
</Grid>
</CippOffCanvas>
</>
);
};
22 changes: 13 additions & 9 deletions src/components/CippComponents/CippIntunePolicyDetails.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,37 +4,41 @@ import CippJsonView from '../CippFormPages/CippJSONView'

export const CippIntunePolicyDetails = ({ row, tenant }) => {
const isConfigurationPolicy = row?.URLName?.toLowerCase() === 'configurationpolicies'
const isAdministrativeTemplate = row?.URLName?.toLowerCase() === 'grouppolicyconfigurations'
const isSupportedPolicyType = isConfigurationPolicy || isAdministrativeTemplate
const urlName = isAdministrativeTemplate ? 'groupPolicyConfigurations' : 'configurationPolicies'
const policyTypeLabel = isAdministrativeTemplate ? 'Administrative Template' : 'Settings Catalog'
const tenantFilter = tenant === 'AllTenants' && row?.Tenant ? row.Tenant : tenant

const policyDetails = ApiGetCall({
url: '/api/ListIntunePolicy',
queryKey: `ListIntunePolicyDetails-${tenantFilter}-${row?.id}`,
queryKey: `ListIntunePolicyDetails-${urlName}-${tenantFilter}-${row?.id}`,
data: {
TenantFilter: tenantFilter,
ID: row?.id,
URLName: 'configurationPolicies',
URLName: urlName,
IncludeSettingDefinitions: true,
},
waiting: Boolean(isConfigurationPolicy && tenantFilter && row?.id),
waiting: Boolean(isSupportedPolicyType && tenantFilter && row?.id),
retry: 1,
refetchOnWindowFocus: false,
toast: false,
})

if (!isConfigurationPolicy) {
if (!isSupportedPolicyType) {
return null
}

const details = Array.isArray(policyDetails.data) ? policyDetails.data[0] : policyDetails.data
const fallbackDetails = row?.settings ? row : null
const settingsObject = details?.settings ? details : fallbackDetails
const fallbackDetails = row?.settings || row?.definitionValues ? row : null
const settingsObject = details?.settings || details?.definitionValues ? details : fallbackDetails

if (policyDetails.isLoading || policyDetails.isFetching) {
return (
<Stack direction="row" spacing={1.5} alignItems="center" sx={{ py: 2 }}>
<CircularProgress size={18} />
<Typography variant="body2" color="text.secondary">
Loading policy settings and Microsoft descriptions...
Loading policy details and Microsoft descriptions...
</Typography>
</Stack>
)
Expand All @@ -43,15 +47,15 @@ export const CippIntunePolicyDetails = ({ row, tenant }) => {
if (policyDetails.isError && !settingsObject) {
return (
<Alert severity="warning" variant="outlined">
Could not load live Settings Catalog details for this policy.
Could not load live {policyTypeLabel} details for this policy.
</Alert>
)
}

if (!settingsObject) {
return (
<Alert severity="info" variant="outlined">
This Settings Catalog policy did not return any settings.
This {policyTypeLabel} policy did not return any settings.
</Alert>
)
}
Expand Down
Loading