Skip to content

Commit

Permalink
webui: introduce dialog to unlock existing locked LUKS partitions
Browse files Browse the repository at this point in the history
  • Loading branch information
KKoukiou committed Jul 11, 2023
1 parent bbd37f6 commit 7e5e0e4
Show file tree
Hide file tree
Showing 7 changed files with 306 additions and 29 deletions.
14 changes: 14 additions & 0 deletions ui/webui/src/apis/storage.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,20 @@ export const getAllDiskSelection = () => {
);
};

/**
* @param {string} deviceName A device name
* @param {string} password A password
*
* @returns {Promise} Resolves true if success otherwise false
*/
export const unlockDevice = ({ deviceName, passphrase }) => {
return new StorageClient().client.call(
"/org/fedoraproject/Anaconda/Modules/Storage/DeviceTree",
"org.fedoraproject.Anaconda.Modules.Storage.DeviceTree.Handler",
"UnlockDevice", [deviceName, passphrase]
);
};

/**
* @param {string} disk A device name
*
Expand Down
24 changes: 14 additions & 10 deletions ui/webui/src/components/app.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ import { LocalizationClient, startEventMonitorLocalization } from "../apis/local
import { StorageClient, initDataStorage, startEventMonitorStorage } from "../apis/storage.js";
import { PayloadsClient } from "../apis/payloads";

import { WithDialogs } from "dialogs.jsx";

import { readBuildstamp, getIsFinal } from "../helpers/betanag.js";
import { readConf } from "../helpers/conf.js";
import { useReducerWithThunk, reducer, initialState } from "../reducer.js";
Expand Down Expand Up @@ -141,16 +143,18 @@ export const Application = () => {
})}
</AlertGroup>}
<AddressContext.Provider value={address}>
<AnacondaWizard
onAddErrorNotification={onAddErrorNotification}
toggleContextHelp={toggleContextHelp}
hideContextHelp={() => setIsHelpExpanded(false)}
title={title}
storageData={state.storage}
localizationData={state.localization}
dispatch={dispatch}
conf={conf}
/>
<WithDialogs>
<AnacondaWizard
onAddErrorNotification={onAddErrorNotification}
toggleContextHelp={toggleContextHelp}
hideContextHelp={() => setIsHelpExpanded(false)}
title={title}
storageData={state.storage}
localizationData={state.localization}
dispatch={dispatch}
conf={conf}
/>
</WithDialogs>
</AddressContext.Provider>
</Page>
);
Expand Down
27 changes: 23 additions & 4 deletions ui/webui/src/components/review/ReviewConfiguration.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,24 @@ export const ReviewDescriptionList = ({ children }) => {
);
};

const DeviceRow = ({ data, requests }) => {
const name = data.name.v;
const checkDeviceInSubTree = (device, rootDevice, deviceData) => {
const parents = device.parents.v;

if (parents.length && parents[0] === rootDevice) {
return true;
} else if (parents.length && parents[0] !== rootDevice) {
return checkDeviceInSubTree(deviceData[parents[0]], rootDevice, deviceData);
} else {
return false;
}
};

const DeviceRow = ({ deviceData, disk, requests }) => {
const [isExpanded, setIsExpanded] = useState(false);

const data = deviceData[disk];
const name = data.name.v;

const renderRow = row => {
const iconColumn = row.reformat.v ? <CheckCircleIcon /> : null;
return {
Expand All @@ -87,7 +101,12 @@ const DeviceRow = ({ data, requests }) => {
};
};

const partitionRows = requests?.filter(req => req["device-spec"].includes(name)).map(renderRow) || [];
const partitionRows = requests?.filter(req => {
const partitionName = Object.keys(deviceData).find(device => deviceData[device].path.v === req["device-spec"]);
const device = deviceData[partitionName];

return checkDeviceInSubTree(device, name, deviceData);
}).map(renderRow) || [];

return (
<DataListItem id={`data-list-${name}`} isExpanded={isExpanded} key={name}>
Expand Down Expand Up @@ -205,7 +224,7 @@ export const ReviewConfiguration = ({ deviceData, diskSelection, language, reque
<Title className="storage-devices-configuration-title" headingLevel="h4">{_("Storage devices and configurations")}</Title>
<DataList isCompact>
{diskSelection.selectedDisks.map(disk => {
return <DeviceRow key={disk} data={deviceData[disk]} requests={storageScenarioId === "custom-mount-point" ? requests : null} />;
return <DeviceRow key={disk} deviceData={deviceData} disk={disk} requests={storageScenarioId === "custom-mount-point" ? requests : null} />;
})}
</DataList>
</ExpandableSection>
Expand Down
66 changes: 53 additions & 13 deletions ui/webui/src/components/storage/CustomMountPoint.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,25 +16,31 @@
*/

import cockpit from "cockpit";
import React, { useState, useEffect } from "react";
import React, { useState, useEffect, useMemo } from "react";

import {
Alert,
Button,
Checkbox,
Flex,
FlexItem,
HelperText,
HelperTextItem,
Popover,
Select,
SelectOption,
SelectVariant,
Text,
TextContent,
TextVariants,
} from "@patternfly/react-core";
import { HelpIcon } from "@patternfly/react-icons";

import { ListingTable } from "cockpit-components-table.jsx";
import { EmptyStatePanel } from "cockpit-components-empty-state.jsx";

import { AnacondaPage } from "../AnacondaPage.jsx";
import { UnlockDialog } from "./UnlockDialog.jsx";

import {
createPartitioning,
Expand Down Expand Up @@ -119,6 +125,20 @@ const MountpointCheckbox = ({ reformat, isRootMountPoint, handleCheckReFormat, p

export const CustomMountPoint = ({ deviceData, diskSelection, partitioningData, dispatch, idPrefix, setIsFormValid, onAddErrorNotification, toggleContextHelp, stepNotification }) => {
const [creatingPartitioning, setCreatingPartitioning] = useState(true);
const [showUnlockDialog, setShowUnlockDialog] = useState(false);

const lockedLUKSPartitions = useMemo(() => {
const devs = partitioningData?.requests?.map(r => r["device-spec"]) || [];

return Object.keys(deviceData).filter(d => {
return (
devs.includes(deviceData[d].path.v) &&
deviceData[d].formatData.type.v === "luks" &&
deviceData[d].formatData.attrs.v.has_key !== "True"
);
});
}, [deviceData, partitioningData?.requests]);

useEffect(() => {
const validateMountPoints = requests => {
if (requests) {
Expand All @@ -136,6 +156,10 @@ export const CustomMountPoint = ({ deviceData, diskSelection, partitioningData,
const partitioningDevicesPaths = partitioningData?.requests.map(r => r["device-spec"]) || [];
const canReusePartitioning = selectedDevicesPaths.length === partitioningDevicesPaths.length && selectedDevicesPaths.every(d => partitioningDevicesPaths.includes(d));

useEffect(() => {
setShowUnlockDialog(lockedLUKSPartitions.length > 0);
}, [lockedLUKSPartitions]);

useEffect(() => {
if (canReusePartitioning) {
setCreatingPartitioning(false);
Expand Down Expand Up @@ -204,16 +228,28 @@ export const CustomMountPoint = ({ deviceData, diskSelection, partitioningData,
const isNotMountPoint = ["biosboot"].includes(row["format-type"]);
// TODO: Anaconda does not support formatting btrfs yet
const isBtrfs = row["format-type"] === "btrfs";
const isLockedLUKS = lockedLUKSPartitions.some(p => row["device-spec"].includes(p));

return {
props: { key: row["device-spec"] },
columns: [
{ title: row["device-spec"] },
{ title: row["format-type"] },
{
title: (
<Flex>
<FlexItem>{row["format-type"]}</FlexItem>
{isLockedLUKS &&
<Button variant="secondary" onClick={() => setShowUnlockDialog(true)} id="unlock-luks-btn">
{_("Unlock")}
</Button>}
</Flex>
)
},
{
title: (
<MountPointSelect
handleOnSelect={handleOnSelect}
isDisabled={isNotMountPoint}
isDisabled={isNotMountPoint || isLockedLUKS}
mountpoint={row["mount-point"]}
partition={row["device-spec"]}
requests={partitioningData.requests}
Expand All @@ -227,7 +263,7 @@ export const CustomMountPoint = ({ deviceData, diskSelection, partitioningData,
isRootMountPoint={isRootMountPoint}
partition={row["device-spec"]}
handleCheckReFormat={handleCheckReFormat}
isDisabled={isNotMountPoint || isBtrfs}
isDisabled={isNotMountPoint || isBtrfs || isLockedLUKS}
/>
)
},
Expand All @@ -245,15 +281,19 @@ export const CustomMountPoint = ({ deviceData, diskSelection, partitioningData,
title={stepNotification.message}
variant="danger"
/>}
<TextContent>
{_("We discovered your partitioned and formatted filesystems, so now you can select your own custom mount point for each filesystem.")}
</TextContent>
<ListingTable
aria-label={_("Partitions")}
columns={[_("Partition"), _("Format type"), _("Mount point"), _("Reformat")]}
emptyCaption={_("No partitions")}
id="custom-mountpoint-table"
rows={partitionRows} />
<>
<TextContent>
<Text component={TextVariants.p}>{_("We discovered your partitioned and formatted filesystems, so now you can select your own custom mount point for each filesystem.")}</Text>
</TextContent>
<ListingTable
aria-label={_("Partitions")}
columns={[_("Partition"), _("Format type"), _("Mount point"), _("Reformat")]}
emptyCaption={_("No partitions")}
id="custom-mountpoint-table"
rows={partitionRows} />
</>
{showUnlockDialog &&
<UnlockDialog dispatch={dispatch} onClose={() => setShowUnlockDialog(false)} partition={lockedLUKSPartitions[0]} />}
</AnacondaPage>
);
};
14 changes: 12 additions & 2 deletions ui/webui/src/components/storage/InstallationDestination.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ import {
Tooltip,
} from "@patternfly/react-core";

import { HelpIcon, LockIcon } from "@patternfly/react-icons";
import { HelpIcon, LockIcon, LockOpenIcon } from "@patternfly/react-icons";

import { EmptyStatePanel } from "cockpit-components-empty-state.jsx";
import { ListingTable } from "cockpit-components-table.jsx";
Expand Down Expand Up @@ -293,6 +293,16 @@ const LocalStandardDisks = ({ deviceData, diskSelection, dispatch, idPrefix, set
},
];

const lockIcon = partition => {
const isLocked = partition.formatData.attrs.v.has_key?.toLowerCase() !== "true";

if (isLocked) {
return <LockIcon />;
} else {
return <LockOpenIcon />;
}
};

const expandedContent = (disk) => (
<ListingTable
variant="compact"
Expand All @@ -303,7 +313,7 @@ const LocalStandardDisks = ({ deviceData, diskSelection, dispatch, idPrefix, set
title: (
<Flex spaceItems={{ default: "spaceItemsSm" }}>
<FlexItem>{partition.path.v}</FlexItem>
{partition.formatData.type.v === "luks" && <FlexItem><LockIcon /></FlexItem>}
{partition.formatData.type.v === "luks" && <FlexItem>{lockIcon(partition)}</FlexItem>}
</Flex>
)
};
Expand Down
Loading

0 comments on commit 7e5e0e4

Please sign in to comment.