Skip to content

Commit f876114

Browse files
authored
Merge pull request #68 from KelvinTegelaar/main
[pull] main from KelvinTegelaar:main
2 parents 7226d9f + f4448a5 commit f876114

10 files changed

Lines changed: 329 additions & 180 deletions

File tree

package.json

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "cipp",
3-
"version": "10.5.0",
3+
"version": "10.5.1",
44
"author": "CIPP Contributors",
55
"homepage": "https://cipp.app/",
66
"bugs": {
@@ -39,22 +39,22 @@
3939
"@musement/iso-duration": "^1.0.0",
4040
"@nivo/core": "^0.99.0",
4141
"@nivo/sankey": "^0.99.0",
42-
"@react-pdf/renderer": "^4.3.2",
42+
"@react-pdf/renderer": "^4.5.1",
4343
"@reduxjs/toolkit": "^2.11.2",
4444
"@tanstack/query-sync-storage-persister": "^5.90.25",
4545
"@tanstack/react-query": "^5.100.10",
4646
"@tanstack/react-query-devtools": "^5.96.2",
4747
"@tanstack/react-query-persist-client": "^5.96.2",
4848
"@tanstack/react-table": "^8.19.2",
4949
"@tiptap/core": "^3.22.3",
50-
"@tiptap/extension-heading": "^3.4.1",
50+
"@tiptap/extension-heading": "^3.22.3",
5151
"@tiptap/extension-table": "^3.20.5",
5252
"@tiptap/pm": "^3.22.3",
5353
"@tiptap/react": "^3.20.5",
5454
"@tiptap/starter-kit": "^3.20.5",
5555
"@vvo/tzdb": "^6.198.0",
5656
"apexcharts": "5.10.4",
57-
"axios": "1.15.0",
57+
"axios": "1.16.1",
5858
"date-fns": "4.1.0",
5959
"diff": "^8.0.3",
6060
"dompurify": "^3.4.3",
@@ -84,7 +84,7 @@
8484
"react-dom": "19.2.6",
8585
"react-dropzone": "15.0.0",
8686
"react-error-boundary": "^6.1.1",
87-
"react-hook-form": "^7.72.0",
87+
"react-hook-form": "^7.76.1",
8888
"react-hot-toast": "2.6.0",
8989
"react-html-parser": "^2.0.2",
9090
"react-leaflet": "5.0.0",
@@ -96,7 +96,7 @@
9696
"react-redux": "9.2.0",
9797
"react-syntax-highlighter": "^16.1.0",
9898
"react-time-ago": "^7.3.3",
99-
"react-virtuoso": "^4.18.5",
99+
"react-virtuoso": "^4.18.7",
100100
"recharts": "^3.8.1",
101101
"redux": "5.0.1",
102102
"redux-persist": "^6.0.0",

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.5.0"
2+
"version": "10.5.1"
33
}

src/components/CippComponents/ForcedSsoMigrationDialog.jsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,9 @@ export const ForcedSsoMigrationDialog = () => {
9999
'SSO migration failed. Please try again.'}
100100
</Alert>
101101
<Typography variant="body2" color="text.secondary">
102-
If this error persists, contact your CIPP administrator.
102+
The app registration may have been created already — clicking <strong>Try Again</strong>{' '}
103+
will pick up where it left off rather than starting over. If the error persists,
104+
contact your CIPP administrator.
103105
</Typography>
104106
</>
105107
) : null}

src/components/CippComponents/SsoMigrationDialog.jsx

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,8 @@ export const SsoMigrationDialog = ({ meData }) => {
6060

6161
const result = ssoSetup.data?.data?.Results ?? ssoSetup.data?.Results
6262
const isSuccess = result?.severity === 'success'
63-
const isError = ssoSetup.isError || result?.severity === 'failed'
63+
const isPartial = result?.severity === 'warning' && result?.canRepair
64+
const isError = ssoSetup.isError || result?.severity === 'failed' || (result?.severity === 'warning' && !result?.canRepair)
6465

6566
return (
6667
<Dialog open={open} onClose={handleClose} maxWidth="sm" fullWidth>
@@ -109,6 +110,20 @@ export const SsoMigrationDialog = ({ meData }) => {
109110
<Alert severity="success" sx={{ mb: 1 }}>
110111
{result.message}
111112
</Alert>
113+
) : isPartial ? (
114+
<Alert severity="warning" sx={{ mb: 1 }}>
115+
<Typography variant="body2" sx={{ fontWeight: 600, mb: 0.5 }}>
116+
App created — secret creation failed
117+
</Typography>
118+
<Typography variant="body2" sx={{ mb: 1 }}>
119+
The CIPP-SSO app registration ({result.appId}) was created successfully, but the
120+
client secret could not be generated. The app ID is saved.
121+
</Typography>
122+
<Typography variant="body2">
123+
Open <strong>Advanced &rarr; Super Admin &rarr; SSO</strong> and click{' '}
124+
<strong>Repair</strong> to finish setup.
125+
</Typography>
126+
</Alert>
112127
) : isError ? (
113128
<Alert severity="error" sx={{ mb: 1 }}>
114129
{result?.message || ssoSetup.error?.message || 'SSO setup failed. It will be retried automatically.'}

src/components/CippFormPages/CippAddEditUser.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -824,7 +824,7 @@ const CippAddEditUser = (props) => {
824824
label: group.displayName,
825825
value: group.id,
826826
addedFields: {
827-
groupType: group.calculatedGroupType || group.groupType,
827+
groupType: group.groupType,
828828
},
829829
})) || []
830830
}

src/components/CippSettings/CippSSOSettings.jsx

Lines changed: 124 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
1-
import { useEffect, useState } from "react";
1+
import { useEffect } from "react";
22
import {
33
Alert,
44
Button,
55
CardActions,
66
CardContent,
7-
CardHeader,
87
Chip,
98
Divider,
109
Skeleton,
@@ -20,16 +19,16 @@ import { CippApiResults } from "../CippComponents/CippApiResults";
2019

2120
const statusLabels = {
2221
none: { label: "Not Configured", color: "default" },
23-
app_created: { label: "App Created", color: "info" },
24-
appid_stored: { label: "App ID Stored", color: "info" },
22+
app_created: { label: "App Created — Secret Pending", color: "warning" },
23+
appid_stored: { label: "App ID Stored — Secret Pending", color: "warning" },
2524
secrets_stored: { label: "Secrets Stored", color: "success" },
2625
complete: { label: "Complete", color: "success" },
2726
error: { label: "Error", color: "error" },
2827
};
2928

30-
export const CippSSOSettings = () => {
31-
const [showCreate, setShowCreate] = useState(false);
29+
const repairableStatuses = new Set(["error", "app_created", "appid_stored"]);
3230

31+
export const CippSSOSettings = () => {
3332
const formControl = useForm({
3433
mode: "onChange",
3534
defaultValues: { multiTenant: false },
@@ -49,32 +48,82 @@ export const CippSSOSettings = () => {
4948
if (ssoStatus.isSuccess && ssoStatus.data?.Results) {
5049
const data = ssoStatus.data.Results;
5150
formControl.reset({ multiTenant: data.multiTenant ?? false });
52-
setShowCreate(!data.configured);
5351
}
5452
}, [ssoStatus.isSuccess, ssoStatus.data]);
5553

56-
const handleUpdate = () => {
54+
const data = ssoStatus.data?.Results;
55+
const statusKey = data?.status ?? "none";
56+
const statusInfo = statusLabels[statusKey] ?? statusLabels.none;
57+
const hasAppId = Boolean(data?.appId);
58+
// Server-provided canRepair is authoritative when present; fall back to local heuristic.
59+
const canRepair =
60+
data?.canRepair ??
61+
(hasAppId && repairableStatuses.has(statusKey));
62+
const isProvisioned =
63+
statusKey === "complete" || (statusKey === "secrets_stored" && hasAppId);
64+
// Show "Create SSO App" whenever there isn't a working app AND there's nothing to repair —
65+
// covers fresh installs AND legacy broken installs where the AppId was never persisted
66+
// (the original "Failed to create client secret after 5 attempts" bug).
67+
const showCreate = !isProvisioned && !canRepair;
68+
const isOrphanedError = statusKey === "error" && !hasAppId;
69+
70+
const handleCreate = () => {
71+
ssoAction.mutate({
72+
url: "/api/ExecSSOSetup",
73+
data: {
74+
Action: "Create",
75+
multiTenant: formControl.getValues("multiTenant"),
76+
},
77+
});
78+
};
79+
80+
const handleRepair = () => {
81+
ssoAction.mutate({
82+
url: "/api/ExecSSOSetup",
83+
data: { Action: "Repair" },
84+
});
85+
};
86+
87+
const handleRecreate = () => {
5788
if (
5889
!window.confirm(
59-
"Updating SSO settings will restart the CIPP instance. Changes may take up to 60 seconds to reflect. Do you want to continue?"
90+
"Recreate will clear the current SSO record and provision a brand new CIPP-SSO app. The previous app registration will be left in your Entra tenant (you can delete it manually). Continue?"
6091
)
6192
) {
6293
return;
6394
}
64-
ssoAction.mutate({
65-
url: "/api/ExecSSOSetup",
66-
data: {
67-
Action: "Update",
68-
multiTenant: formControl.getValues("multiTenant"),
95+
// Clear first, then create. ApiPostCall chains via the success refetch — call sequentially.
96+
ssoAction.mutate(
97+
{
98+
url: "/api/ExecSSOSetup",
99+
data: { Action: "Recreate" },
69100
},
70-
});
101+
{
102+
onSuccess: () => {
103+
ssoAction.mutate({
104+
url: "/api/ExecSSOSetup",
105+
data: {
106+
Action: "Create",
107+
multiTenant: formControl.getValues("multiTenant"),
108+
},
109+
});
110+
},
111+
}
112+
);
71113
};
72114

73-
const handleCreate = () => {
115+
const handleUpdate = () => {
116+
if (
117+
!window.confirm(
118+
"Updating SSO settings will restart the CIPP instance. Changes may take up to 60 seconds to reflect. Do you want to continue?"
119+
)
120+
) {
121+
return;
122+
}
74123
ssoAction.mutate({
75124
url: "/api/ExecSSOSetup",
76125
data: {
77-
Action: "Create",
126+
Action: "Update",
78127
multiTenant: formControl.getValues("multiTenant"),
79128
},
80129
});
@@ -87,9 +136,6 @@ export const CippSSOSettings = () => {
87136
});
88137
};
89138

90-
const data = ssoStatus.data?.Results;
91-
const statusInfo = statusLabels[data?.status] ?? statusLabels.none;
92-
93139
return (
94140
<CippButtonCard title="SSO App Registration" isFetching={ssoStatus.isFetching}>
95141
<CardContent>
@@ -141,13 +187,38 @@ export const CippSSOSettings = () => {
141187
)}
142188

143189
{data?.lastError && (
144-
<>
145-
<Grid size={{ xs: 12 }}>
146-
<Alert severity="error" sx={{ mt: 1 }}>
147-
{data.lastError}
148-
</Alert>
149-
</Grid>
150-
</>
190+
<Grid size={{ xs: 12 }}>
191+
<Alert
192+
severity={canRepair ? "warning" : "error"}
193+
sx={{ mt: 1 }}
194+
>
195+
<Typography variant="body2" sx={{ fontWeight: 600, mb: 0.5 }}>
196+
{canRepair
197+
? "Setup did not finish"
198+
: isOrphanedError
199+
? "Previous setup failed"
200+
: "Error"}
201+
</Typography>
202+
<Typography variant="body2">{data.lastError}</Typography>
203+
{canRepair && (
204+
<Typography variant="caption" sx={{ display: "block", mt: 1 }}>
205+
The app registration ({data.appId}) was created successfully but the
206+
client secret could not be generated. Click <strong>Repair</strong> to
207+
retry the secret on the existing app, or <strong>Recreate</strong> to
208+
start over with a fresh app registration.
209+
</Typography>
210+
)}
211+
{isOrphanedError && (
212+
<Typography variant="caption" sx={{ display: "block", mt: 1 }}>
213+
A previous attempt to set up SSO did not save an App ID, so there's
214+
nothing to repair. An orphaned <strong>CIPP-SSO</strong> app
215+
registration may exist in your Entra tenant — you can delete it
216+
manually. Click <strong>Create SSO App</strong> to provision a fresh
217+
app registration.
218+
</Typography>
219+
)}
220+
</Alert>
221+
</Grid>
151222
)}
152223
</Grid>
153224

@@ -158,6 +229,7 @@ export const CippSSOSettings = () => {
158229
name="multiTenant"
159230
label="Multi-tenant mode (allow users from multiple Entra ID tenants)"
160231
formControl={formControl}
232+
disabled={!isProvisioned && !showCreate}
161233
/>
162234

163235
<CippApiResults apiObject={ssoAction} />
@@ -167,15 +239,38 @@ export const CippSSOSettings = () => {
167239
{!ssoStatus.isLoading && (
168240
<CardActions sx={{ justifyContent: "flex-end", px: 2, pb: 2 }}>
169241
<Stack direction="row" spacing={1}>
170-
{showCreate ? (
242+
{showCreate && (
171243
<Button
172244
variant="contained"
173245
onClick={handleCreate}
174246
disabled={ssoAction.isPending}
175247
>
176248
Create SSO App
177249
</Button>
178-
) : (
250+
)}
251+
252+
{canRepair && (
253+
<>
254+
<Button
255+
variant="outlined"
256+
color="warning"
257+
onClick={handleRecreate}
258+
disabled={ssoAction.isPending}
259+
>
260+
Recreate
261+
</Button>
262+
<Button
263+
variant="contained"
264+
color="warning"
265+
onClick={handleRepair}
266+
disabled={ssoAction.isPending}
267+
>
268+
Repair
269+
</Button>
270+
</>
271+
)}
272+
273+
{isProvisioned && (
179274
<>
180275
<Button
181276
variant="outlined"

src/components/CippTable/CippDataTable.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -600,8 +600,13 @@ export const CippDataTable = (props) => {
600600
[simple, hasActions, hasOffCanvas, hasOnChange, maxHeightOffset, settings?.tablePageSize?.value]
601601
)
602602

603-
// Include updateTrigger in data memo to force re-render when license backfill completes
604-
const memoizedData = useMemo(() => usedData, [usedData, updateTrigger])
603+
// Include updateTrigger in data memo to force re-render when license backfill completes.
604+
// Also refresh data identity when derived columns change so TanStack re-runs filtering
605+
// for searches entered before columns are available.
606+
const memoizedData = useMemo(
607+
() => (Array.isArray(usedData) ? usedData.slice() : usedData),
608+
[usedData, updateTrigger, usedColumns]
609+
)
605610

606611
// Sanitize columnVisibility to remove any undefined/invalid keys before passing to MRT
607612
const sanitizedColumnVisibility = useMemo(() => {

src/data/standards.json

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1791,7 +1791,7 @@
17911791
"name": "standards.AppManagementPolicy",
17921792
"cat": "Entra (AAD) Standards",
17931793
"tag": ["CIS M365 7.0.0 (5.1.5.3)", "CIS M365 7.0.0 (5.1.5.4)", "CIS M365 7.0.0 (5.1.5.5)", "CIS M365 7.0.0 (5.1.5.6)"],
1794-
"appliesToTest": ["CIS_5_1_5_3", "CIS_5_1_5_4", "CIS_5_1_5_5", "CIS_5_1_5_6"],
1794+
"appliesToTest": ["CIS_5_1_5_3", "CIS_5_1_5_4", "CIS_5_1_5_5", "CIS_5_1_5_6", "ZTNA21773", "ZTNA21896", "ZTNA21992"],
17951795
"helpText": "Configures the default app management policy to control application and service principal credential restrictions such as password and key credential lifetimes.",
17961796
"docsDescription": "Configures the default app management policy to control application and service principal credential restrictions. This includes password addition restrictions, custom password addition, symmetric key addition, and credential lifetime limits for both applications and service principals.",
17971797
"executiveText": "Enforces credential restrictions on application registrations and service principals to limit how secrets and certificates are created and how long they remain valid. This reduces the risk of long-lived or unmanaged credentials being used to access your tenant.",
@@ -1838,8 +1838,7 @@
18381838
"impactColour": "warning",
18391839
"addedDate": "2026-03-13",
18401840
"powershellEquivalent": "Graph API",
1841-
"recommendedBy": [],
1842-
"appliesToTest": ["ZTNA21773", "ZTNA21896", "ZTNA21992"]
1841+
"recommendedBy": []
18431842
},
18441843
{
18451844
"name": "standards.OutBoundSpamAlert",

src/pages/teams-share/sharepoint/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ const Page = () => {
2525
const reportDB = useCippReportDB({
2626
apiUrl: '/api/ListSites?type=SharePointSiteUsage',
2727
queryKey: 'ListSites-SharePointSiteUsage',
28-
cacheName: 'Sites',
28+
cacheName: 'SharePointSiteUsage',
2929
syncTitle: 'Sync SharePoint Sites Report',
3030
syncData: { Types: 'SharePointSiteUsage' },
3131
allowToggle: true,

0 commit comments

Comments
 (0)