Skip to content

Commit adaa7e4

Browse files
authored
Merge pull request #658 from KelvinTegelaar/dev
[pull] dev from KelvinTegelaar:dev
2 parents 4a86f5d + 8a18279 commit adaa7e4

File tree

6 files changed

+191
-3
lines changed

6 files changed

+191
-3
lines changed

src/components/CippIntegrations/CippIntegrationTenantMapping.jsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {
1010
Typography,
1111
} from "@mui/material";
1212
import { Grid } from "@mui/system";
13-
import { useState } from "react";
13+
import { useState, useMemo } from "react";
1414
import { useForm } from "react-hook-form";
1515
import { ApiGetCall, ApiPostCall } from "/src/api/ApiCall";
1616
import { useRouter } from "next/router";
@@ -144,6 +144,11 @@ const CippIntegrationSettings = ({ children }) => {
144144

145145
const extension = extensions.find((extension) => extension.id === router.query.id);
146146

147+
// Memoize the removeOptions array to ensure it updates when tableData changes
148+
const removedTenantIds = useMemo(() => {
149+
return Array.isArray(tableData) ? tableData.map((item) => item.TenantId) : [];
150+
}, [tableData]);
151+
147152
useEffect(() => {
148153
if (mappings.isSuccess) {
149154
setTableData(mappings.data.Mappings ?? []);
@@ -173,7 +178,7 @@ const CippIntegrationSettings = ({ children }) => {
173178
multiple={false}
174179
required={false}
175180
disableClearable={false}
176-
removeOptions={tableData.map((item) => item.TenantId)}
181+
removeOptions={removedTenantIds}
177182
valueField="customerId"
178183
/>
179184
</Box>
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
import { Button, ButtonGroup, SvgIcon, Typography, TextField, Box } from "@mui/material";
2+
import CippButtonCard from "/src/components/CippCards/CippButtonCard";
3+
import { ApiGetCall, ApiPostCall } from "/src/api/ApiCall";
4+
import { History } from "@mui/icons-material";
5+
import { useState, useEffect } from "react";
6+
7+
const CippBackupRetentionSettings = () => {
8+
const retentionSetting = ApiGetCall({
9+
url: "/api/ExecBackupRetentionConfig?List=true",
10+
queryKey: "BackupRetentionSettings",
11+
});
12+
13+
const retentionChange = ApiPostCall({
14+
datafromUrl: true,
15+
relatedQueryKeys: "BackupRetentionSettings",
16+
});
17+
18+
const [retentionDays, setRetentionDays] = useState(30);
19+
const [error, setError] = useState("");
20+
21+
useEffect(() => {
22+
if (retentionSetting?.data?.Results?.RetentionDays) {
23+
setRetentionDays(retentionSetting.data.Results.RetentionDays);
24+
}
25+
}, [retentionSetting.data]);
26+
27+
const handleRetentionChange = () => {
28+
const days = parseInt(retentionDays);
29+
30+
if (isNaN(days) || days < 7) {
31+
setError("Retention must be at least 7 days");
32+
return;
33+
}
34+
35+
setError("");
36+
retentionChange.mutate({
37+
url: "/api/ExecBackupRetentionConfig",
38+
data: { RetentionDays: days },
39+
queryKey: "BackupRetentionPost",
40+
});
41+
};
42+
43+
const handleInputChange = (e) => {
44+
const value = e.target.value;
45+
setRetentionDays(value);
46+
47+
const days = parseInt(value);
48+
if (!isNaN(days) && days < 7) {
49+
setError("Retention must be at least 7 days");
50+
} else if (isNaN(days)) {
51+
setError("Please enter a valid number");
52+
} else {
53+
setError("");
54+
}
55+
};
56+
57+
const RetentionControls = () => {
58+
return (
59+
<Box sx={{ display: "flex", gap: 1, alignItems: "flex-start" }}>
60+
<TextField
61+
size="small"
62+
type="number"
63+
value={retentionDays}
64+
onChange={handleInputChange}
65+
disabled={retentionChange.isPending || retentionSetting.isLoading}
66+
inputProps={{ min: 7 }}
67+
error={!!error}
68+
helperText={error}
69+
sx={{ width: "120px" }}
70+
label="Days"
71+
/>
72+
<Button
73+
variant="contained"
74+
color="primary"
75+
size="small"
76+
disabled={retentionChange.isPending || retentionSetting.isLoading || !!error}
77+
onClick={handleRetentionChange}
78+
sx={{ mt: 0.5 }}
79+
>
80+
Save
81+
</Button>
82+
</Box>
83+
);
84+
};
85+
86+
return (
87+
<CippButtonCard
88+
title="Backup Retention"
89+
cardSx={{ display: "flex", flexDirection: "column", height: "100%" }}
90+
CardButton={<RetentionControls />}
91+
>
92+
<Typography variant="body2">
93+
Configure how long to keep backup files. Both CIPP system backups and tenant backups will be
94+
automatically deleted after this period. Minimum retention is 7 days, default is 30 days.
95+
Cleanup runs daily at 2:00 AM.
96+
</Typography>
97+
</CippButtonCard>
98+
);
99+
};
100+
101+
export default CippBackupRetentionSettings;

src/data/alerts.json

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,26 @@
239239
"recommendedRunInterval": "7d",
240240
"description": "Monitors Global Admin accounts and alerts when they don't have an alternate email address set, which is important for password recovery of key accounts."
241241
},
242+
{
243+
"name": "GlobalAdminAllowList",
244+
"label": "Alert on Global Admins outside approved list",
245+
"recommendedRunInterval": "4h",
246+
"requiresInput": true,
247+
"multipleInput": true,
248+
"inputs": [
249+
{
250+
"inputType": "textField",
251+
"inputLabel": "Approved Global Admin UPN prefixes (comma separated)",
252+
"inputName": "ApprovedGlobalAdmins"
253+
},
254+
{
255+
"inputType": "switch",
256+
"inputLabel": "Alert per non-compliant admin? (off = single aggregated alert)",
257+
"inputName": "AlertEachAdmin"
258+
}
259+
],
260+
"description": "Alerts when Global Administrator accounts are present whose UPN prefix (before @domain) is not in your approved comma-separated allow list. Toggle per-admin alerts to get one alert per user or a single aggregated alert."
261+
},
242262
{
243263
"name": "NewRiskyUsers",
244264
"label": "Alert on new risky users (P2 License Required)",

src/pages/cipp/settings/index.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import CippDnsSettings from "/src/components/CippSettings/CippDnsSettings";
1010
import CippCacheSettings from "/src/components/CippSettings/CippCacheSettings";
1111
import CippBackupSettings from "/src/components/CippSettings/CippBackupSettings";
1212
import CippBrandingSettings from "/src/components/CippSettings/CippBrandingSettings";
13+
import CippBackupRetentionSettings from "/src/components/CippSettings/CippBackupRetentionSettings";
1314
const Page = () => {
1415
return (
1516
<Container sx={{ pt: 3 }} maxWidth="xl">
@@ -29,6 +30,9 @@ const Page = () => {
2930
<Grid size={{ lg: 4, md: 6, sm: 12, xs: 12 }}>
3031
<CippBackupSettings />
3132
</Grid>
33+
<Grid size={{ lg: 4, md: 6, sm: 12, xs: 12 }}>
34+
<CippBackupRetentionSettings />
35+
</Grid>
3236
<Grid size={{ lg: 4, md: 6, sm: 12, xs: 12 }}>
3337
<CippBrandingSettings />
3438
</Grid>

src/pages/endpoint/MEM/devices/index.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,38 @@ const Page = () => {
169169
condition: (row) => row.operatingSystem === "macOS",
170170
confirmText: "Are you sure you want to retrieve the FileVault key for [deviceName]?",
171171
},
172+
{
173+
label: "Reset Passcode",
174+
type: "POST",
175+
icon: <PasswordOutlined />,
176+
url: "/api/ExecDevicePasscodeAction",
177+
data: {
178+
GUID: "id",
179+
Action: "resetPasscode",
180+
},
181+
condition: (row) =>
182+
row.operatingSystem === "iOS" ||
183+
row.operatingSystem === "macOS" ||
184+
row.operatingSystem === "Android",
185+
confirmText:
186+
"Are you sure you want to reset the passcode for [deviceName]? A new passcode will be generated and displayed.",
187+
},
188+
{
189+
label: "Remove Passcode",
190+
type: "POST",
191+
icon: <Password />,
192+
url: "/api/ExecDevicePasscodeAction",
193+
data: {
194+
GUID: "id",
195+
Action: "removeDevicePasscode",
196+
},
197+
condition: (row) =>
198+
row.operatingSystem === "iOS" ||
199+
row.operatingSystem === "macOS" ||
200+
row.operatingSystem === "Android",
201+
confirmText:
202+
"Are you sure you want to remove the passcode from [deviceName]? This will remove the device passcode requirement.",
203+
},
172204
{
173205
label: "Windows Defender Full Scan",
174206
type: "POST",

src/pages/identity/administration/jit-admin/index.js

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,31 @@ import { AdminPanelSettings } from "@mui/icons-material";
55
import Link from "next/link";
66

77
const Page = () => {
8+
const simpleColumns = [
9+
"userPrincipalName",
10+
"displayName",
11+
"accountEnabled",
12+
"jitAdminEnabled",
13+
"jitAdminStartDate",
14+
"jitAdminExpiration",
15+
"jitAdminReason",
16+
"jitAdminCreatedBy",
17+
"memberOf",
18+
];
19+
20+
const filters = [
21+
{
22+
filterName: "Active JIT Admins",
23+
value: [{ id: "jitAdminEnabled", value: true }],
24+
type: "column",
25+
},
26+
{
27+
filterName: "Expired/Disabled",
28+
value: [{ id: "jitAdminEnabled", value: false }],
29+
type: "column",
30+
},
31+
];
32+
833
return (
934
<CippTablePage
1035
cardButton={
@@ -17,7 +42,8 @@ const Page = () => {
1742
title="JIT Admin Table"
1843
apiUrl="/api/ListJITAdmin"
1944
apiDataKey="Results"
20-
simpleColumns={[]}
45+
simpleColumns={simpleColumns}
46+
filters={filters}
2147
/>
2248
);
2349
};

0 commit comments

Comments
 (0)