Skip to content

Commit 2219a7a

Browse files
Merge pull request KelvinTegelaar#5556 from KelvinTegelaar/dev
Dev to release
2 parents a831dd6 + 412a4a1 commit 2219a7a

File tree

73 files changed

+4220
-1505
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

73 files changed

+4220
-1505
lines changed
Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
---
2-
name: "Assign Issue to Volunteer"
3-
on: [issue_comment] # yamllint disable-line rule:truthy
2+
name: "Issue volunteer assignment"
3+
on:
4+
issue_comment:
5+
types: [created]
46
jobs:
5-
build:
7+
volunteer:
68
runs-on: ubuntu-slim
79
steps:
8-
- uses: bhermann/issue-volunteer@v0.1.20
10+
- uses: kris6673/issue-volunteer@v0.2.0
911
with:
1012
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"

.github/workflows/Node_Project_Check.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ jobs:
2020
steps:
2121
- uses: actions/checkout@v6
2222
- name: Use Node.js ${{ matrix.node-version }}
23-
uses: actions/setup-node@v6.2.0
23+
uses: actions/setup-node@v6.3.0
2424
with:
2525
node-version: ${{ matrix.node-version }}
2626
- name: Install and Build Test

.github/workflows/cipp_dev_build.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ jobs:
2626
echo "node_version=$node_sanitized_version" >> $GITHUB_OUTPUT
2727
2828
- name: Set up Node.js
29-
uses: actions/setup-node@v6.2.0
29+
uses: actions/setup-node@v6.3.0
3030
with:
3131
node-version: ${{ steps.get_node_version.outputs.node_version }}
3232

@@ -47,7 +47,7 @@ jobs:
4747
4848
# Upload to Azure Blob Storage
4949
- name: Azure Blob Upload
50-
uses: LanceMcCarthy/Action-AzureBlobUpload@v3.7.0
50+
uses: LanceMcCarthy/Action-AzureBlobUpload@v3.8.0
5151
with:
5252
connection_string: ${{ secrets.AZURE_CONNECTION_STRING }}
5353
container_name: cipp

.github/workflows/cipp_frontend_build.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ jobs:
2626
echo "node_version=$node_sanitized_version" >> $GITHUB_OUTPUT
2727
2828
- name: Set up Node.js
29-
uses: actions/setup-node@v6.2.0
29+
uses: actions/setup-node@v6.3.0
3030
with:
3131
node-version: ${{ steps.get_node_version.outputs.node_version }}
3232

@@ -47,7 +47,7 @@ jobs:
4747
4848
# Upload to Azure Blob Storage
4949
- name: Azure Blob Upload
50-
uses: LanceMcCarthy/Action-AzureBlobUpload@v3.7.0
50+
uses: LanceMcCarthy/Action-AzureBlobUpload@v3.8.0
5151
with:
5252
connection_string: ${{ secrets.AZURE_CONNECTION_STRING }}
5353
container_name: cipp

package.json

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "cipp",
3-
"version": "10.1.2",
3+
"version": "10.2.0",
44
"author": "CIPP Contributors",
55
"homepage": "https://cipp.app/",
66
"bugs": {
@@ -77,15 +77,15 @@
7777
"numeral": "2.0.6",
7878
"prop-types": "15.8.1",
7979
"punycode": "^2.3.1",
80-
"react": "19.2.3",
81-
"react-apexcharts": "1.7.0",
80+
"react": "19.2.4",
81+
"react-apexcharts": "1.9.0",
8282
"react-beautiful-dnd": "13.1.1",
8383
"react-copy-to-clipboard": "^5.1.0",
84-
"react-dom": "19.2.3",
84+
"react-dom": "19.2.4",
8585
"react-dropzone": "14.3.8",
86-
"react-error-boundary": "^6.1.0",
87-
"react-grid-layout": "^1.5.0",
88-
"react-hook-form": "^7.71.1",
86+
"react-error-boundary": "^6.1.1",
87+
"react-grid-layout": "^2.2.2",
88+
"react-hook-form": "^7.71.2",
8989
"react-hot-toast": "2.6.0",
9090
"react-html-parser": "^2.0.2",
9191
"react-i18next": "16.2.4",
@@ -118,4 +118,4 @@
118118
"eslint": "9.39.2",
119119
"eslint-config-next": "16.1.6"
120120
}
121-
}
121+
}

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

src/components/CippCards/CippUniversalSearchV2.jsx

Lines changed: 144 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -10,27 +10,32 @@ import {
1010
CircularProgress,
1111
InputAdornment,
1212
Portal,
13+
Button,
1314
} from "@mui/material";
1415
import { Search as SearchIcon } from "@mui/icons-material";
1516
import { ApiGetCall } from "../../api/ApiCall";
16-
import { useSettings } from "../../hooks/use-settings";
1717
import { useRouter } from "next/router";
1818
import { BulkActionsMenu } from "../bulk-actions-menu";
19-
import { Button } from "@mui/material";
19+
import { CippOffCanvas } from "../CippComponents/CippOffCanvas";
20+
import { CippBitlockerKeySearch } from "../CippComponents/CippBitlockerKeySearch";
2021

2122
export const CippUniversalSearchV2 = React.forwardRef(
2223
({ onConfirm = () => {}, onChange = () => {}, maxResults = 10, value = "" }, ref) => {
2324
const [searchValue, setSearchValue] = useState(value);
2425
const [searchType, setSearchType] = useState("Users");
26+
const [bitlockerLookupType, setBitlockerLookupType] = useState("keyId");
2527
const [showDropdown, setShowDropdown] = useState(false);
28+
const [bitlockerDrawerVisible, setBitlockerDrawerVisible] = useState(false);
29+
const [bitlockerDrawerDefaults, setBitlockerDrawerDefaults] = useState({
30+
searchTerm: "",
31+
searchType: "keyId",
32+
});
2633
const [dropdownPosition, setDropdownPosition] = useState({ top: 0, left: 0, width: 0 });
2734
const containerRef = useRef(null);
2835
const textFieldRef = useRef(null);
2936
const router = useRouter();
30-
const settings = useSettings();
31-
const { currentTenant } = settings;
3237

33-
const search = ApiGetCall({
38+
const universalSearch = ApiGetCall({
3439
url: `/api/ExecUniversalSearchV2`,
3540
data: {
3641
searchTerms: searchValue,
@@ -41,6 +46,17 @@ export const CippUniversalSearchV2 = React.forwardRef(
4146
waiting: false,
4247
});
4348

49+
const bitlockerSearch = ApiGetCall({
50+
url: "/api/ExecBitlockerSearch",
51+
data: {
52+
[bitlockerLookupType]: searchValue,
53+
},
54+
queryKey: `bitlocker-universal-${bitlockerLookupType}-${searchValue}`,
55+
waiting: false,
56+
});
57+
58+
const activeSearch = searchType === "BitLocker" ? bitlockerSearch : universalSearch;
59+
4460
const handleChange = (event) => {
4561
const newValue = event.target.value;
4662
setSearchValue(newValue);
@@ -71,7 +87,7 @@ export const CippUniversalSearchV2 = React.forwardRef(
7187
const handleSearch = () => {
7288
if (searchValue.length > 0) {
7389
updateDropdownPosition();
74-
search.refetch();
90+
activeSearch.refetch();
7591
setShowDropdown(true);
7692
}
7793
};
@@ -93,6 +109,21 @@ export const CippUniversalSearchV2 = React.forwardRef(
93109

94110
const handleTypeChange = (type) => {
95111
setSearchType(type);
112+
if (type === "BitLocker") {
113+
setBitlockerLookupType("keyId");
114+
}
115+
setShowDropdown(false);
116+
};
117+
118+
const handleBitlockerResultClick = (match) => {
119+
setBitlockerDrawerDefaults({
120+
searchTerm:
121+
bitlockerLookupType === "deviceId"
122+
? match?.deviceId || searchValue
123+
: match?.keyId || searchValue,
124+
searchType: bitlockerLookupType,
125+
});
126+
setBitlockerDrawerVisible(true);
96127
setShowDropdown(false);
97128
};
98129

@@ -107,6 +138,24 @@ export const CippUniversalSearchV2 = React.forwardRef(
107138
icon: "Group",
108139
onClick: () => handleTypeChange("Groups"),
109140
},
141+
{
142+
label: "BitLocker",
143+
icon: "FilePresent",
144+
onClick: () => handleTypeChange("BitLocker"),
145+
},
146+
];
147+
148+
const bitlockerLookupActions = [
149+
{
150+
label: "Key ID",
151+
icon: "FilePresent",
152+
onClick: () => setBitlockerLookupType("keyId"),
153+
},
154+
{
155+
label: "Device ID",
156+
icon: "Laptop",
157+
onClick: () => setBitlockerLookupType("deviceId"),
158+
},
110159
];
111160

112161
// Close dropdown when clicking outside
@@ -144,14 +193,23 @@ export const CippUniversalSearchV2 = React.forwardRef(
144193
}
145194
}, [showDropdown]);
146195

147-
const hasResults = Array.isArray(search?.data) && search.data.length > 0;
196+
const bitlockerResults = Array.isArray(bitlockerSearch?.data?.Results)
197+
? bitlockerSearch.data.Results
198+
: [];
199+
const universalResults = Array.isArray(universalSearch?.data) ? universalSearch.data : [];
200+
const hasResults =
201+
searchType === "BitLocker" ? bitlockerResults.length > 0 : universalResults.length > 0;
148202
const shouldShowDropdown = showDropdown && searchValue.length > 0;
149203

150204
const getLabel = () => {
151205
if (searchType === "Users") {
152206
return "Search users by UPN or Display Name";
153207
} else if (searchType === "Groups") {
154208
return "Search groups by Display Name";
209+
} else if (searchType === "BitLocker") {
210+
return bitlockerLookupType === "deviceId"
211+
? "Search BitLocker by Device ID"
212+
: "Search BitLocker by Recovery Key ID";
155213
}
156214
return "Search";
157215
};
@@ -163,6 +221,12 @@ export const CippUniversalSearchV2 = React.forwardRef(
163221
buttonName={searchType}
164222
actions={typeMenuActions}
165223
/>
224+
{searchType === "BitLocker" && (
225+
<BulkActionsMenu
226+
buttonName={bitlockerLookupType === "deviceId" ? "Device ID" : "Key ID"}
227+
actions={bitlockerLookupActions}
228+
/>
229+
)}
166230
<TextField
167231
ref={(node) => {
168232
textFieldRef.current = node;
@@ -187,7 +251,7 @@ export const CippUniversalSearchV2 = React.forwardRef(
187251
<SearchIcon color="action" sx={{ fontSize: 20 }} />
188252
</InputAdornment>
189253
),
190-
endAdornment: search.isFetching ? (
254+
endAdornment: activeSearch.isFetching ? (
191255
<InputAdornment position="end">
192256
<CircularProgress size={20} />
193257
</InputAdornment>
@@ -203,7 +267,7 @@ export const CippUniversalSearchV2 = React.forwardRef(
203267
<Button
204268
variant="contained"
205269
onClick={handleSearch}
206-
disabled={searchValue.length === 0 || search.isFetching}
270+
disabled={searchValue.length === 0 || activeSearch.isFetching}
207271
startIcon={<SearchIcon />}
208272
sx={{ flexShrink: 0 }}
209273
>
@@ -229,18 +293,25 @@ export const CippUniversalSearchV2 = React.forwardRef(
229293
borderColor: "divider",
230294
}}
231295
>
232-
{search.isFetching ? (
296+
{activeSearch.isFetching ? (
233297
<Box sx={{ p: 2 }}>
234298
<Skeleton height={60} sx={{ mb: 1 }} />
235299
<Skeleton height={60} />
236300
</Box>
237301
) : hasResults ? (
238-
<Results
239-
items={search.data}
240-
searchValue={searchValue}
241-
onResultClick={handleResultClick}
242-
searchType={searchType}
243-
/>
302+
searchType === "BitLocker" ? (
303+
<BitlockerResults
304+
items={bitlockerResults}
305+
onResultClick={handleBitlockerResultClick}
306+
/>
307+
) : (
308+
<Results
309+
items={universalResults}
310+
searchValue={searchValue}
311+
onResultClick={handleResultClick}
312+
searchType={searchType}
313+
/>
314+
)
244315
) : (
245316
<Box sx={{ p: 3, textAlign: "center" }}>
246317
<Typography variant="body2" color="text.secondary">
@@ -251,6 +322,20 @@ export const CippUniversalSearchV2 = React.forwardRef(
251322
</Paper>
252323
</Portal>
253324
)}
325+
326+
<CippOffCanvas
327+
title="BitLocker Key Details"
328+
visible={bitlockerDrawerVisible}
329+
onClose={() => setBitlockerDrawerVisible(false)}
330+
size="xl"
331+
contentPadding={0}
332+
>
333+
<CippBitlockerKeySearch
334+
initialSearchTerm={bitlockerDrawerDefaults.searchTerm}
335+
initialSearchType={bitlockerDrawerDefaults.searchType}
336+
autoSearch={true}
337+
/>
338+
</CippOffCanvas>
254339
</>
255340
);
256341
},
@@ -337,3 +422,46 @@ const Results = ({ items = [], searchValue, onResultClick, searchType = "Users"
337422
</>
338423
);
339424
};
425+
426+
const BitlockerResults = ({ items = [], onResultClick }) => {
427+
return (
428+
<>
429+
{items.map((result, index) => (
430+
<MenuItem
431+
key={result.keyId || index}
432+
onClick={() => onResultClick(result)}
433+
sx={{
434+
py: 1.5,
435+
px: 2,
436+
borderBottom: index < items.length - 1 ? "1px solid" : "none",
437+
borderColor: "divider",
438+
"&:hover": {
439+
backgroundColor: "action.hover",
440+
},
441+
}}
442+
>
443+
<ListItemText
444+
primary={
445+
<Typography variant="body1" fontWeight="medium">
446+
{result.deviceName || "Unknown Device"}
447+
</Typography>
448+
}
449+
secondary={
450+
<Box>
451+
<Typography variant="body2" color="text.secondary">
452+
Key ID: {result.keyId || "N/A"}
453+
</Typography>
454+
<Typography variant="body2" color="text.secondary">
455+
Device ID: {result.deviceId || "N/A"}
456+
</Typography>
457+
<Typography variant="caption" color="text.secondary" sx={{ display: "block", mt: 0.5 }}>
458+
Tenant: {result.tenant || "N/A"}
459+
</Typography>
460+
</Box>
461+
}
462+
/>
463+
</MenuItem>
464+
))}
465+
</>
466+
);
467+
};

src/components/CippCards/CippUserInfoCard.jsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,17 @@ import { getCippFormatting } from "../../utils/get-cipp-formatting";
1818
import { Stack, Grid, Box } from "@mui/system";
1919
import { useState, useRef, useCallback } from "react";
2020
import { ApiPostCall } from "../../api/ApiCall";
21+
import { useLicenseBackfill } from "../../hooks/use-license-backfill";
2122

2223
export const CippUserInfoCard = (props) => {
2324
const { user, tenant, isFetching = false, ...other } = props;
2425
const [photoTimestamp, setPhotoTimestamp] = useState(Date.now());
2526
const [uploadError, setUploadError] = useState(null);
2627
const [successMessage, setSuccessMessage] = useState(null);
2728
const fileInputRef = useRef(null);
29+
30+
// Hook to trigger re-render when license backfill completes
31+
const { updateTrigger } = useLicenseBackfill();
2832

2933
// API mutations
3034
const setPhotoMutation = ApiPostCall({ urlFromData: true });
@@ -280,6 +284,7 @@ export const CippUserInfoCard = (props) => {
280284
<PropertyListItem
281285
divider
282286
label="Licenses"
287+
key={`licenses-${updateTrigger}`}
283288
value={
284289
isFetching ? (
285290
<Skeleton variant="text" width={180} />

0 commit comments

Comments
 (0)