Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Cloud Security] Azure integration manual fields #171069

Merged
merged 14 commits into from
Nov 29, 2023
Merged
23 changes: 20 additions & 3 deletions x-pack/plugins/cloud_security_posture/common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import {
VulnSeverity,
AwsCredentialsTypeFieldMap,
GcpCredentialsTypeFieldMap,
AzureCredentialsTypeFieldMap,
} from './types';

export const STATUS_ROUTE_PATH = '/internal/cloud_security_posture/status';
Expand Down Expand Up @@ -161,7 +160,25 @@ export const GCP_CREDENTIALS_TYPE_TO_FIELDS_MAP: GcpCredentialsTypeFieldMap = {
'credentials-json': ['gcp.credentials.json'],
};

export const AZURE_CREDENTIALS_TYPE_TO_FIELDS_MAP: AzureCredentialsTypeFieldMap = {
manual: [],
export const AZURE_CREDENTIALS_TYPE_TO_FIELDS_MAP = {
arm_template: [],
service_principal_with_client_secret: [
'azure.credentials.tenant_id',
'azure.credentials.client_id',
'azure.credentials.client_secret',
],
service_principal_with_client_certificate: [
'azure.credentials.tenant_id',
'azure.credentials.client_id',
'azure.credentials.client_certificate_path',
'azure.credentials.client_certificate_password',
],
service_principal_with_client_username_and_password: [
'azure.credentials.tenant_id',
'azure.credentials.client_id',
'azure.credentials.client_username',
'azure.credentials.client_password',
],
managed_identity: [],
manual: [],
};
8 changes: 7 additions & 1 deletion x-pack/plugins/cloud_security_posture/common/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,13 @@ export type GcpCredentialsTypeFieldMap = {
[key in GcpCredentialsType]: string[];
};

export type AzureCredentialsType = 'arm_template' | 'manual';
export type AzureCredentialsType =
| 'arm_template'
| 'service_principal_with_client_secret'
| 'service_principal_with_client_certificate'
| 'service_principal_with_client_username_and_password'
| 'managed_identity'
| 'manual';

export type AzureCredentialsTypeFieldMap = {
[key in AzureCredentialsType]: string[];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ export const cleanupCredentials = (packagePolicy: NewPackagePolicy | UpdatePacka
const azureCredentialType: AzureCredentialsType | undefined =
enabledInput?.streams?.[0].vars?.['azure.credentials.type']?.value;

if (awsCredentialType || gcpCredentialType) {
if (awsCredentialType || gcpCredentialType || azureCredentialType) {
let credsToKeep: string[] = [' '];
let credFields: string[] = [' '];
if (awsCredentialType) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,18 @@
* 2.0.
*/
import React, { useEffect } from 'react';
import { EuiLink, EuiSpacer, EuiText, EuiTitle, EuiCallOut, EuiHorizontalRule } from '@elastic/eui';
import {
EuiLink,
EuiSpacer,
EuiText,
EuiTitle,
EuiCallOut,
EuiHorizontalRule,
EuiFormRow,
EuiSelect,
EuiFieldPassword,
EuiFieldText,
} from '@elastic/eui';
import type { NewPackagePolicy } from '@kbn/fleet-plugin/public';
import { NewPackagePolicyInput, PackageInfo } from '@kbn/fleet-plugin/common';
import { FormattedMessage } from '@kbn/i18n-react';
Expand All @@ -14,8 +25,13 @@ import { i18n } from '@kbn/i18n';
import semverValid from 'semver/functions/valid';
import semverCoerce from 'semver/functions/coerce';
import semverLt from 'semver/functions/lt';
import {
AzureOptions,
getAzureCredentialsFormManualOptions,
} from './get_azure_credentials_form_options';
import { AzureCredentialsType } from '../../../../common/types';
import { SetupFormat, useAzureCredentialsForm } from './hooks';
import { NewPackagePolicyPostureInput } from '../utils';
import { getPosturePolicy, NewPackagePolicyPostureInput } from '../utils';
import { CspRadioOption, RadioGroup } from '../csp_boxed_radio_group';

interface AzureSetupInfoContentProps {
Expand Down Expand Up @@ -161,7 +177,31 @@ const ArmTemplateSetup = ({
);
};

const ManualSetup = ({ integrationLink }: { integrationLink: string }) => {
const AzureCredentialTypeSelector = ({
type,
onChange,
}: {
onChange(type: AzureCredentialsType): void;
type: AzureCredentialsType;
}) => (
<EuiFormRow
fullWidth
label={i18n.translate('xpack.csp.azureIntegration.azureCredentialTypeSelectorLabel', {
defaultMessage: 'Preferred manual method',
})}
>
<EuiSelect
fullWidth
options={getAzureCredentialsFormManualOptions()}
value={type}
onChange={(optionElem) => {
onChange(optionElem.target.value as AzureCredentialsType);
}}
/>
</EuiFormRow>
);

const TemporaryManualSetup = ({ integrationLink }: { integrationLink: string }) => {
return (
<>
<EuiText color="subdued" size="s">
Expand Down Expand Up @@ -206,6 +246,53 @@ const ManualSetup = ({ integrationLink }: { integrationLink: string }) => {
};

const AZURE_MINIMUM_PACKAGE_VERSION = '1.6.0';
const AZURE_MANUAL_FIELDS_PACKAGE_VERSION = '1.7.0';

export const getDefaultAzureManualCredentialType = (packageInfo: PackageInfo) => {
const packageSemanticVersion = semverValid(packageInfo.version);
const cleanPackageVersion = semverCoerce(packageSemanticVersion) || '';

const isPackageVersionValidForManualFields = !semverLt(
cleanPackageVersion,
AZURE_MANUAL_FIELDS_PACKAGE_VERSION
);

return isPackageVersionValidForManualFields ? 'managed_identity' : 'manual';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: I find it a bit confusing that when "package is valid for manual" we return managed identity otherwise manual. without the context, I would think it should be the other way around. Maybe change to isPackageVersionValidForManagedIdentity?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

umm this is a very good point, thing is that "manual fields" are meant to reference the entire options (fields) under the Manual credential type, managed identity is just one of them and it happens to be the default value out of those options.

do you think something like isPackageVersionValidForManualOption is better? i dont mind sticking with your suggestions, just wanted to give more context and see what do you think

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, I see where the confusion is coming from, that we have Manual as a term that includes multiple setup methods, and inside it we have another Manual as its own setup method, as in "really manual, you need to do everything yourself" kind of thing. changing to isPackageVersionValidForManualOption tbh wouldn't change much for me, in the end starting from 1.7.0 we started to support Manual -> Managed Identity so it became the default, and before it was Manual -> Manual the default one. So isPackageVersionValidForManagedIdentity still makes more sense to me (even though it's not only Managed Identity but some other options we started to support) but I'm also ok with a comment in this function to explain this Manual extravaganza, without changing the name :)

};

const AzureInputVarFields = ({
fields,
onChange,
}: {
fields: Array<AzureOptions[keyof AzureOptions]['fields'][number] & { value: string; id: string }>;
onChange: (key: string, value: string) => void;
}) => (
<div>
{fields.map((field) => (
<EuiFormRow key={field.id} label={field.label} fullWidth hasChildLabel={true} id={field.id}>
<>
{field.type === 'password' && (
<EuiFieldPassword
id={field.id}
type="dual"
fullWidth
value={field.value || ''}
onChange={(event) => onChange(field.id, event.target.value)}
/>
)}
{field.type === 'text' && (
<EuiFieldText
id={field.id}
fullWidth
value={field.value || ''}
onChange={(event) => onChange(field.id, event.target.value)}
/>
)}
</>
</EuiFormRow>
))}
</div>
);

export const AzureCredentialsForm = ({
input,
Expand All @@ -216,15 +303,22 @@ export const AzureCredentialsForm = ({
setIsValid,
disabled,
}: Props) => {
const { setupFormat, onSetupFormatChange, integrationLink, hasArmTemplateUrl } =
useAzureCredentialsForm({
newPolicy,
input,
packageInfo,
onChange,
setIsValid,
updatePolicy,
});
const {
group,
fields,
azureCredentialsType,
setupFormat,
onSetupFormatChange,
integrationLink,
hasArmTemplateUrl,
} = useAzureCredentialsForm({
newPolicy,
input,
packageInfo,
onChange,
setIsValid,
updatePolicy,
});

useEffect(() => {
if (!setupFormat) {
Expand All @@ -238,6 +332,10 @@ export const AzureCredentialsForm = ({
cleanPackageVersion,
AZURE_MINIMUM_PACKAGE_VERSION
);
const isPackageVersionValidForManualFields = !semverLt(
cleanPackageVersion,
AZURE_MANUAL_FIELDS_PACKAGE_VERSION
);

useEffect(() => {
setIsValid(isPackageVersionValidForAzure);
Expand Down Expand Up @@ -280,8 +378,52 @@ export const AzureCredentialsForm = ({
{setupFormat === AZURE_ARM_TEMPLATE_CREDENTIAL_TYPE && (
<ArmTemplateSetup hasArmTemplateUrl={hasArmTemplateUrl} input={input} />
)}
{setupFormat === AZURE_MANUAL_CREDENTIAL_TYPE && (
<ManualSetup integrationLink={integrationLink} />
{setupFormat === AZURE_MANUAL_CREDENTIAL_TYPE && !isPackageVersionValidForManualFields && (
<TemporaryManualSetup integrationLink={integrationLink} />
)}
{setupFormat === AZURE_MANUAL_CREDENTIAL_TYPE && isPackageVersionValidForManualFields && (
<>
<AzureCredentialTypeSelector
type={azureCredentialsType}
onChange={(optionId) => {
updatePolicy(
getPosturePolicy(newPolicy, input.type, {
'azure.credentials.type': { value: optionId },
})
);
}}
/>
<EuiSpacer size="m" />
<AzureInputVarFields
fields={fields}
onChange={(key, value) => {
updatePolicy(getPosturePolicy(newPolicy, input.type, { [key]: { value } }));
}}
/>
<EuiSpacer size="m" />
{group.info}
<EuiSpacer size="m" />
<EuiText color="subdued" size="s">
<FormattedMessage
id="xpack.csp.azureIntegration.manualCredentialType.documentaion"
defaultMessage="Read the {documentation} for more details"
values={{
documentation: (
<EuiLink
href={ARM_TEMPLATE_EXTERNAL_DOC_URL}
target="_blank"
rel="noopener nofollow noreferrer"
data-test-subj="externalLink"
>
{i18n.translate('xpack.csp.azureIntegration.documentationLinkText', {
defaultMessage: 'documentation',
})}
</EuiLink>
),
}}
/>
</EuiText>
</>
)}
<EuiSpacer />
</>
Expand Down
Loading
Loading