diff --git a/web/src/api/storage.ts b/web/src/api/storage.ts index d67166e828..ffa26f375b 100644 --- a/web/src/api/storage.ts +++ b/web/src/api/storage.ts @@ -23,6 +23,7 @@ import { get, post } from "~/api/http"; import { Job } from "~/types/job"; import { calculate, fetchSettings } from "~/api/storage/proposal"; +import { config } from "~/api/storage/types"; /** * Starts the storage probing process. @@ -33,6 +34,9 @@ const probe = (): Promise => post("/api/storage/probe"); export { probe }; +const fetchConfig = (): Promise => + get("/api/storage/config").then((config) => config.storage); + /** * Returns the list of jobs */ @@ -57,4 +61,4 @@ const refresh = async (): Promise => { await calculate(settings); }; -export { fetchStorageJobs, findStorageJob, refresh }; +export { fetchConfig, fetchStorageJobs, findStorageJob, refresh }; diff --git a/web/src/api/storage/types.ts b/web/src/api/storage/types.ts index aee104ea76..44db12a9b7 100644 --- a/web/src/api/storage/types.ts +++ b/web/src/api/storage/types.ts @@ -436,3 +436,7 @@ export type SetProposalSettingsResponse = boolean; export type UsableDevicesResponse = Array; export type PingResponse2 = PingResponse; + +import * as config from "./types/config"; + +export { config }; diff --git a/web/src/api/storage/types/config.ts b/web/src/api/storage/types/config.ts new file mode 100644 index 0000000000..7a7864a01d --- /dev/null +++ b/web/src/api/storage/types/config.ts @@ -0,0 +1,369 @@ +/* eslint-disable */ +/** + * This file was automatically generated by json-schema-to-typescript. + * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file, + * and run json-schema-to-typescript to regenerate this file. + */ + +export type DriveElement = FormattedDrive | PartitionedDrive; +export type SearchElement = SimpleSearchAll | SimpleSearchByName | AdvancedSearch; +/** + * Shortcut to match all devices if there is any (equivalent to specify no conditions and to skip the entry if no device is found). + */ +export type SimpleSearchAll = "*"; +export type SimpleSearchByName = string; +/** + * How to handle the section if the device is not found. + */ +export type SearchAction = "skip" | "error"; +/** + * Alias used to reference a device. + */ +export type Alias = string; +export type Encryption = + | EncryptionLuks1 + | EncryptionLuks2 + | EncryptionPervasiveLuks2 + | EncryptionTPM + | EncryptionSwap; +/** + * Password to use when creating a new encryption device. + */ +export type EncryptionPassword = string; +/** + * The value must be compatible with the --cipher argument of the command cryptsetup. + */ +export type EncryptionCipher = string; +/** + * The value (in bits) has to be a multiple of 8. The possible key sizes are limited by the used cipher. + */ +export type EncryptionKeySize = number; +export type EncryptionPbkdFunction = "pbkdf2" | "argon2i" | "argon2id"; +/** + * Swap encryptions. + */ +export type EncryptionSwap = "protected_swap" | "secure_swap" | "random_swap"; +export type FilesystemType = FilesystemTypeAny | FilesystemTypeBtrfs; +export type FilesystemTypeAny = + | "bcachefs" + | "btrfs" + | "exfat" + | "ext2" + | "ext3" + | "ext4" + | "f2fs" + | "jfs" + | "nfs" + | "nilfs2" + | "ntfs" + | "reiserfs" + | "swap" + | "tmpfs" + | "vfat" + | "xfs"; +/** + * How to mount the device. + */ +export type MountBy = "device" | "id" | "label" | "path" | "uuid"; +/** + * Partition table type. + */ +export type PtableType = "gpt" | "msdos" | "dasd"; +export type PartitionElement = + | SimpleVolumesGenerator + | AdvancedPartitionsGenerator + | RegularPartition + | PartitionToDelete + | PartitionToDeleteIfNeeded; +export type PartitionId = "linux" | "swap" | "lvm" | "raid" | "esp" | "prep" | "bios_boot"; +export type Size = SizeValue | SizeTuple | SizeRange; +export type SizeValue = SizeString | SizeBytes; +/** + * Human readable size. + */ +export type SizeString = string; +/** + * Size in bytes. + */ +export type SizeBytes = number; +/** + * Lower size limit and optionally upper size limit. + * + * @minItems 1 + * @maxItems 2 + */ +export type SizeTuple = [SizeValueWithCurrent] | [SizeValueWithCurrent, SizeValueWithCurrent]; +export type SizeValueWithCurrent = SizeValue | SizeCurrent; +/** + * The current size of the device. + */ +export type SizeCurrent = "current"; +export type PhysicalVolumeElement = + | Alias + | SimplePhysicalVolumesGenerator + | AdvancedPhysicalVolumesGenerator; +export type LogicalVolumeElement = + | SimpleVolumesGenerator + | AdvancedLogicalVolumesGenerator + | LogicalVolume + | ThinPoolLogicalVolume + | ThinLogicalVolume; +/** + * Number of stripes. + */ +export type LogicalVolumeStripes = number; + +/** + * Storage config. + */ +export interface Config { + boot?: Boot; + /** + * Drives (disks, BIOS RAIDs and multipath devices). + */ + drives?: DriveElement[]; + /** + * LVM volume groups. + */ + volumeGroups?: VolumeGroup[]; +} +/** + * Allows configuring boot partitions automatically. + */ +export interface Boot { + /** + * Whether to configure partitions for booting. + */ + configure: boolean; + /** + * The target installation device is used by default. + */ + device?: string; +} +/** + * Drive without a partition table (e.g., directly formatted). + */ +export interface FormattedDrive { + search?: SearchElement; + alias?: Alias; + encryption?: Encryption; + filesystem: Filesystem; +} +/** + * Advanced options for searching devices. + */ +export interface AdvancedSearch { + condition?: SearchCondition; + /** + * Maximum devices to match. + */ + max?: number; + ifNotFound?: SearchAction; +} +export interface SearchCondition { + name: SimpleSearchByName; +} +/** + * LUKS1 encryption. + */ +export interface EncryptionLuks1 { + luks1: { + password: EncryptionPassword; + cipher?: EncryptionCipher; + keySize?: EncryptionKeySize; + }; +} +/** + * LUKS2 encryption. + */ +export interface EncryptionLuks2 { + luks2: { + password: EncryptionPassword; + cipher?: EncryptionCipher; + keySize?: EncryptionKeySize; + pbkdFunction?: EncryptionPbkdFunction; + /** + * LUKS2 label. + */ + label?: string; + }; +} +/** + * LUKS2 pervasive encryption. + */ +export interface EncryptionPervasiveLuks2 { + pervasiveLuks2: { + password: EncryptionPassword; + }; +} +/** + * TPM-Based Full Disk Encrytion. + */ +export interface EncryptionTPM { + tpmFde: { + password: EncryptionPassword; + }; +} +export interface Filesystem { + /** + * Try to reuse the existing file system. In some cases the file system could not be reused, for example, if the device is re-encrypted. + */ + reuseIfPossible?: boolean; + type?: FilesystemType; + /** + * File system label. + */ + label?: string; + /** + * Mount path. + */ + path?: string; + mountBy?: MountBy; + /** + * Options for creating the file system. + */ + mkfsOptions?: string[]; + /** + * Options to add to the fourth field of fstab. + */ + mountOptions?: string[]; +} +/** + * Btrfs file system. + */ +export interface FilesystemTypeBtrfs { + btrfs: { + /** + * Whether to configrue Btrfs snapshots. + */ + snapshots?: boolean; + }; +} +export interface PartitionedDrive { + search?: SearchElement; + alias?: Alias; + ptableType?: PtableType; + partitions?: PartitionElement[]; +} +/** + * Automatically creates the default or mandatory volumes configured by the selected product. + */ +export interface SimpleVolumesGenerator { + generate: "default" | "mandatory"; +} +/** + * Creates the default or mandatory partitions configured by the selected product. + */ +export interface AdvancedPartitionsGenerator { + generate: { + partitions: "default" | "mandatory"; + encryption?: Encryption; + }; +} +export interface RegularPartition { + search?: SearchElement; + alias?: Alias; + id?: PartitionId; + size?: Size; + encryption?: Encryption; + filesystem?: Filesystem; +} +/** + * Size range. + */ +export interface SizeRange { + min: SizeValueWithCurrent; + max?: SizeValueWithCurrent; +} +export interface PartitionToDelete { + search: SearchElement; + /** + * Delete the partition. + */ + delete: true; +} +export interface PartitionToDeleteIfNeeded { + search: SearchElement; + /** + * Delete the partition if needed to make space. + */ + deleteIfNeeded: true; + size?: Size; +} +/** + * LVM volume group. + */ +export interface VolumeGroup { + /** + * Volume group name. + */ + name?: string; + extentSize?: SizeValue; + /** + * Devices to use as physical volumes. + */ + physicalVolumes?: PhysicalVolumeElement[]; + logicalVolumes?: LogicalVolumeElement[]; +} +/** + * Automatically creates the needed physical volumes in the indicated devices. + */ +export interface SimplePhysicalVolumesGenerator { + generate: Alias[]; +} +/** + * Automatically creates the needed physical volumes in the indicated devices. + */ +export interface AdvancedPhysicalVolumesGenerator { + generate: { + targetDevices: Alias[]; + encryption?: Encryption; + }; +} +/** + * Automatically creates the default or mandatory logical volumes configured by the selected product. + */ +export interface AdvancedLogicalVolumesGenerator { + generate: { + logicalVolumes: "default" | "mandatory"; + encryption?: Encryption; + stripes?: LogicalVolumeStripes; + stripeSize?: SizeValue; + }; +} +export interface LogicalVolume { + /** + * Logical volume name. + */ + name?: string; + size?: Size; + stripes?: LogicalVolumeStripes; + stripeSize?: SizeValue; + encryption?: Encryption; + filesystem?: Filesystem; +} +export interface ThinPoolLogicalVolume { + /** + * LVM thin pool. + */ + pool: true; + alias?: Alias; + /** + * Logical volume name. + */ + name?: string; + size?: Size; + stripes?: LogicalVolumeStripes; + stripeSize?: SizeValue; + encryption?: Encryption; +} +export interface ThinLogicalVolume { + /** + * Thin logical volume name. + */ + name?: string; + size?: Size; + usedPool: Alias; + encryption?: Encryption; + filesystem?: Filesystem; +} diff --git a/web/src/components/core/InstallationFinished.tsx b/web/src/components/core/InstallationFinished.tsx index d61e5f5e96..598ed705ca 100644 --- a/web/src/components/core/InstallationFinished.tsx +++ b/web/src/components/core/InstallationFinished.tsx @@ -38,11 +38,10 @@ import { Text, } from "@patternfly/react-core"; import { Center, Icon } from "~/components/layout"; -import { EncryptionMethods } from "~/types/storage"; import { _ } from "~/i18n"; import alignmentStyles from "@patternfly/react-styles/css/utilities/Alignment/alignment"; import { useInstallerStatus } from "~/queries/status"; -import { useProposalResult } from "~/queries/storage"; +import { useConfig } from "~/queries/storage"; import { finishInstallation } from "~/api/manager"; import { InstallationPhase } from "~/types/status"; import { Navigate } from "react-router-dom"; @@ -77,11 +76,28 @@ the machine needs to boot directly to the new boot loader.", const SuccessIcon = () => ; +// TODO: define some utility method to get the device used as root (drive, partition, logical volume). +// TODO: use type checking for config. +function usingTpm(config): boolean { + const { guided, drives = [], volumeGroups = [] } = config; + + if (guided !== undefined) { + return guided.encryption?.method === "tpm_fde"; + } + const devices = [ + ...drives, + ...drives.flatMap((d) => d.partitions || []), + ...volumeGroups.flatMap((v) => v.logicalVolumes || []), + ]; + + const root = devices.find((d) => d.filesystem?.path === "/"); + + return root?.encryption?.tpmFde !== undefined; +} + function InstallationFinished() { const { phase, isBusy, useIguana } = useInstallerStatus({ suspense: true }); - const { - settings: { encryptionPassword, encryptionMethod }, - } = useProposalResult(); + const config = useConfig(); if (phase !== InstallationPhase.Install) { return ; @@ -91,8 +107,6 @@ function InstallationFinished() { return ; } - const usingTpm = encryptionPassword?.length > 0 && encryptionMethod === EncryptionMethods.TPM; - return (
@@ -119,7 +133,7 @@ function InstallationFinished() { "At this point you can reboot the machine to log in to the new system.", )} - {usingTpm && } + {usingTpm(config) && } diff --git a/web/src/queries/storage.ts b/web/src/queries/storage.ts index ee58f1c77c..10814d6b80 100644 --- a/web/src/queries/storage.ts +++ b/web/src/queries/storage.ts @@ -29,6 +29,7 @@ import { } from "@tanstack/react-query"; import React from "react"; import { fetchDevices, fetchDevicesDirty } from "~/api/storage/devices"; +import { fetchConfig } from "~/api/storage"; import { calculate, fetchActions, @@ -123,6 +124,17 @@ const useDevices = ( return data; }; +const configQuery = { + queryKey: ["storage", "config"], + queryFn: fetchConfig, +}; + +const useConfig = () => { + const { data } = useSuspenseQuery(configQuery); + console.log(data); + return data; +}; + /** * Hook that returns the list of available devices for installation. */ @@ -349,6 +361,7 @@ const useDeprecatedChanges = () => { }; export { + useConfig, useDevices, useAvailableDevices, useProductParams,