diff --git a/src/Components/CreateImageWizard/steps/Oscap/Oscap.tsx b/src/Components/CreateImageWizard/steps/Oscap/Oscap.tsx
index 911aec2d..763349f1 100644
--- a/src/Components/CreateImageWizard/steps/Oscap/Oscap.tsx
+++ b/src/Components/CreateImageWizard/steps/Oscap/Oscap.tsx
@@ -1,4 +1,4 @@
-import React, { useState } from 'react';
+import React, { useEffect, useState } from 'react';
import {
Alert,
@@ -18,6 +18,7 @@ import { v4 as uuidv4 } from 'uuid';
import OscapProfileInformation from './OscapProfileInformation';
+import { usePoliciesQuery, PolicyRead } from '../../../../store/complianceApi';
import { useAppDispatch, useAppSelector } from '../../../../store/hooks';
import {
DistributionProfileItem,
@@ -29,9 +30,11 @@ import {
Services,
} from '../../../../store/imageBuilderApi';
import {
- changeOscapProfile,
+ changeCompliance,
selectDistribution,
- selectProfile,
+ selectComplianceProfileID,
+ selectCompliancePolicyID,
+ selectCompliancePolicyTitle,
addPackage,
addPartition,
changeFileSystemConfigurationType,
@@ -42,47 +45,125 @@ import {
changeMaskedServices,
changeDisabledServices,
changeKernelAppend,
+ selectComplianceType,
} from '../../../../store/wizardSlice';
import { useHasSpecificTargetOnly } from '../../utilities/hasSpecificTargetOnly';
import { parseSizeUnit } from '../../utilities/parseSizeUnit';
import { Partition, Units } from '../FileSystem/FileSystemConfiguration';
+const OpenSCAPFGLabel = () => {
+ return (
+ <>
+ OpenSCAP profile
+
+
+ To run a manual compliance scan in OpenSCAP, download this image.
+
+
+ }
+ >
+
+
+ >
+ );
+};
+
const ProfileSelector = () => {
- const oscapProfile = useAppSelector(selectProfile);
+ const policyID = useAppSelector(selectCompliancePolicyID);
+ const policyTitle = useAppSelector(selectCompliancePolicyTitle);
+ const profileID = useAppSelector(selectComplianceProfileID);
const release = useAppSelector(selectDistribution);
+ const majorVersion = release.split('-')[1];
const hasWslTargetOnly = useHasSpecificTargetOnly('wsl');
const dispatch = useAppDispatch();
const [isOpen, setIsOpen] = useState(false);
+ const complianceType = useAppSelector(selectComplianceType);
+
const {
data: profiles,
isFetching,
isSuccess,
isError,
refetch,
- } = useGetOscapProfilesQuery({
- distribution: release,
- });
+ } = useGetOscapProfilesQuery(
+ {
+ distribution: release,
+ },
+ {
+ skip: complianceType === 'compliance',
+ }
+ );
+
+ const {
+ data: policies,
+ isFetching: isFetchingPolicies,
+ isSuccess: isSuccessPolicies,
+ } = usePoliciesQuery(
+ {
+ filter: `os_major_version=${majorVersion}`,
+ },
+ {
+ skip: complianceType === 'openscap',
+ }
+ );
const { data: currentProfileData } = useGetOscapCustomizationsQuery(
{
distribution: release,
// @ts-ignore if openScapProfile is undefined the query is going to get skipped
- profile: oscapProfile,
+ profile: profileID,
},
- { skip: !oscapProfile }
+ { skip: !profileID }
);
const [trigger] = useLazyGetOscapCustomizationsQuery();
+ useEffect(() => {
+ if (!policies || policies.data === undefined) {
+ return;
+ }
+
+ if (policyID && !policyTitle) {
+ for (const p of policies.data) {
+ const pol = p as PolicyRead;
+ if (pol.id === policyID) {
+ dispatch(
+ changeCompliance({
+ policyID: pol.id,
+ profileID: pol.ref_id,
+ policyTitle: pol.title,
+ })
+ );
+ }
+ }
+ }
+ }, [isSuccessPolicies]);
+
const handleToggle = () => {
- if (!isOpen) {
+ if (!isOpen && complianceType === 'openscap') {
refetch();
}
setIsOpen(!isOpen);
};
const handleClear = () => {
- dispatch(changeOscapProfile(undefined));
+ dispatch(
+ changeCompliance({
+ profileID: undefined,
+ policyID: undefined,
+ policyTitle: undefined,
+ })
+ );
clearOscapPackages(currentProfileData?.packages || []);
dispatch(changeFileSystemConfigurationType('automatic'));
handleServices(undefined);
@@ -142,9 +223,9 @@ const ProfileSelector = () => {
const handleSelect = (
_event: React.MouseEvent,
- selection: OScapSelectOptionValueType
+ selection: OScapSelectOptionValueType | ComplianceSelectOptionValueType
) => {
- if (selection.id === undefined) {
+ if (selection.profileID === undefined) {
// handle user has selected 'None' case
handleClear();
} else {
@@ -152,7 +233,7 @@ const ProfileSelector = () => {
trigger(
{
distribution: release,
- profile: selection.id,
+ profile: selection.profileID as DistributionProfileItem,
},
true // preferCacheValue
)
@@ -164,7 +245,24 @@ const ProfileSelector = () => {
handlePackages(oldOscapPackages, newOscapPackages);
handleServices(response.services);
dispatch(changeKernelAppend(response.kernel?.append || ''));
- dispatch(changeOscapProfile(selection.id));
+ if (complianceType === 'openscap') {
+ dispatch(
+ changeCompliance({
+ profileID: selection.profileID,
+ policyID: undefined,
+ policyTitle: undefined,
+ })
+ );
+ } else {
+ const compl = selection as ComplianceSelectOptionValueType;
+ dispatch(
+ changeCompliance({
+ profileID: compl.profileID,
+ policyID: compl.policyID,
+ policyTitle: compl.toString(),
+ })
+ );
+ }
});
}
setIsOpen(false);
@@ -180,67 +278,77 @@ const ProfileSelector = () => {
}
};
+ const complianceOptions = () => {
+ if (!policies || policies.data === undefined) {
+ return [];
+ }
+
+ const res = [];
+ for (const p of policies.data) {
+ if (p === undefined) {
+ continue;
+ }
+ const pol = p as PolicyRead;
+ res.push();
+ }
+ return res;
+ };
+
return (
- OpenSCAP profile
-
-
- To run a manual compliance scan in OpenSCAP, download this
- image.
-
-
- }
- >
-
-
- >
- }
+ label={complianceType === 'openscap' ? : <>Policy>}
>
-
+ )}
{isError && (
string;
};
@@ -291,7 +399,7 @@ const OScapSelectOption = ({
id: DistributionProfileItem,
name?: string
): OScapSelectOptionValueType => ({
- id,
+ profileID: id,
toString: () => name || '',
});
@@ -303,9 +411,52 @@ const OScapSelectOption = ({
/>
);
};
+type ComplianceSelectOptionPropType = {
+ policy: PolicyRead;
+};
+
+type ComplianceSelectOptionValueType = {
+ policyID: string;
+ profileID: string;
+ toString: () => string;
+};
+
+const ComplianceNoneOption = () => {
+ return (
+ 'None', compareTo: () => false }} />
+ );
+};
+
+const ComplianceSelectOption = ({ policy }: ComplianceSelectOptionPropType) => {
+ const selectObj = (
+ policyID: string,
+ profileID: string,
+ title: string
+ ): ComplianceSelectOptionValueType => ({
+ policyID,
+ profileID,
+ toString: () => title,
+ });
+
+ const descr = (
+ <>
+ Threshold: {policy.compliance_threshold}
+
+ Active systems: {policy.total_system_count}
+ >
+ );
+
+ return (
+
+ );
+};
export const Oscap = () => {
- const oscapProfile = useAppSelector(selectProfile);
+ const oscapProfile = useAppSelector(selectComplianceProfileID);
const environments = useAppSelector(selectImageTypes);
return (
diff --git a/src/Components/CreateImageWizard/steps/Oscap/index.tsx b/src/Components/CreateImageWizard/steps/Oscap/index.tsx
index 68279558..3516b03b 100644
--- a/src/Components/CreateImageWizard/steps/Oscap/index.tsx
+++ b/src/Components/CreateImageWizard/steps/Oscap/index.tsx
@@ -1,21 +1,44 @@
import React, { useEffect } from 'react';
-import { Button, Form, Text, Title } from '@patternfly/react-core';
+import {
+ Button,
+ Form,
+ FormGroup,
+ Radio,
+ Text,
+ Title,
+} from '@patternfly/react-core';
import { ExternalLinkAltIcon } from '@patternfly/react-icons';
+import { useFlag } from '@unleash/proxy-client-react';
import { Oscap } from './Oscap';
-import { COMPLIANCE_AND_VULN_SCANNING_URL } from '../../../../constants';
+import {
+ COMPLIANCE_AND_VULN_SCANNING_URL,
+ COMPLIANCE_PROD_URL,
+ COMPLIANCE_STAGE_URL,
+} from '../../../../constants';
import { imageBuilderApi } from '../../../../store/enhancedImageBuilderApi';
-import { useAppSelector } from '../../../../store/hooks';
-import { selectDistribution } from '../../../../store/wizardSlice';
+import { useAppDispatch, useAppSelector } from '../../../../store/hooks';
+import {
+ ComplianceType,
+ changeComplianceType,
+ selectDistribution,
+ selectComplianceType,
+} from '../../../../store/wizardSlice';
+import { useGetEnvironment } from '../../../../Utilities/useGetEnvironment';
const OscapStep = () => {
+ const dispatch = useAppDispatch();
+ const complianceEnabled = useFlag('image-builder.compliance.enabled');
+ const complianceType = useAppSelector(selectComplianceType);
const prefetchOscapProfile = imageBuilderApi.usePrefetch(
'getOscapProfiles',
{}
);
+ const { isProd } = useGetEnvironment();
const release = useAppSelector(selectDistribution);
+
useEffect(() => {
prefetchOscapProfile({ distribution: release });
// This useEffect hook should run *only* on mount and therefore has an empty
@@ -26,24 +49,78 @@ const OscapStep = () => {
return (
);
diff --git a/src/constants.ts b/src/constants.ts
index 910c15b1..c1883042 100644
--- a/src/constants.ts
+++ b/src/constants.ts
@@ -24,6 +24,10 @@ export const RELEASE_LIFECYCLE_URL =
'https://access.redhat.com/support/policy/updates/errata';
export const AZURE_AUTH_URL =
'https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow';
+export const COMPLIANCE_PROD_URL =
+ 'https://console.redhat.com/insights/compliance/scappolicies';
+export const COMPLIANCE_STAGE_URL =
+ 'https://console.stage.redhat.com/insights/compliance/scappolicies';
export const ACTIVATION_KEYS_PROD_URL =
'https://console.redhat.com/insights/connector/activation-keys';
export const ACTIVATION_KEYS_STAGE_URL =