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

Merge latest from dev #7926

Merged
merged 15 commits into from
Dec 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion client-react/src/models/site/site.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ export enum ClientCertMode {
Required = 'Required',
Optional = 'Optional',
OptionalInteractiveUser = 'OptionalInteractiveUser',
Ignore = 'Ignore',
}

export enum MinTlsVersion {
Expand Down Expand Up @@ -139,7 +140,7 @@ export interface Site {
clientAffinityEnabled: boolean;
clientAffinityProxyEnabled: boolean;
clientCertEnabled: boolean;
clientCertMode: ClientCertMode;
clientCertMode: string;
clientCertExclusionPaths: string;
hostNamesDisabled: boolean;
domainVerificationIdentifiers: string;
Expand Down
14 changes: 2 additions & 12 deletions client-react/src/pages/app/app-settings/AppSettingsDataLoader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ const AppSettingsDataLoader: React.FC<AppSettingsDataLoaderProps> = props => {
}

const isLinux = isLinuxApp(site.data);
const windowsContainer = isWindowsContainer(site.data);

// Get stacks response
if (!loadingFailed) {
if (isFunctionApp(site.data)) {
Expand Down Expand Up @@ -247,20 +247,10 @@ const AppSettingsDataLoader: React.FC<AppSettingsDataLoaderProps> = props => {
setEditable(false);
}

const sshEnabled = site.data.properties.sshEnabled;
const functionsRuntimeAdminIsolationEnabled: boolean = !!site.data.properties.functionsRuntimeAdminIsolationEnabled;

setInitialValues({
...convertStateToForm({
// @note(krmitta): Manually over-writing since the api returns null when sshEnabled property is not set in the database but the default is true
site: {
...site.data,
properties: {
...site.data.properties,
sshEnabled: (isLinux || windowsContainer) && sshEnabled === null ? true : sshEnabled,
functionsRuntimeAdminIsolationEnabled: functionsRuntimeAdminIsolationEnabled,
},
},
site: site.data,
config: webConfig.data,
metadata: metadata.metadata.success ? metadata.data : null,
connectionStrings: connectionStrings.metadata.success ? connectionStrings.data : null,
Expand Down
39 changes: 36 additions & 3 deletions client-react/src/pages/app/app-settings/AppSettingsFormData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
} from './AppSettings.types';
import { sortBy, isEqual } from 'lodash-es';
import { ArmArray, ArmObj } from '../../../models/arm-obj';
import { Site, PublishingCredentialPolicies, MinTlsVersion } from '../../../models/site/site';
import { Site, PublishingCredentialPolicies, MinTlsVersion, ClientCertMode } from '../../../models/site/site';
import {
SiteConfig,
ArmAzureStorageMount,
Expand All @@ -29,7 +29,7 @@ import { NameValuePair } from '../../../models/name-value-pair';
import StringUtils from '../../../utils/string';
import { CommonConstants } from '../../../utils/CommonConstants';
import { KeyValue } from '../../../models/portal-models';
import { isFlexConsumption, isFunctionApp, isWindowsCode } from '../../../utils/arm-utils';
import { isFlexConsumption, isFunctionApp, isLinuxApp, isWindowsCode, isWindowsContainer } from '../../../utils/arm-utils';
import { IconConstants } from '../../../utils/constants/IconConstants';
import { ThemeExtended } from '../../../theme/SemanticColorsExtended';
import { TFunction } from 'i18next';
Expand Down Expand Up @@ -93,7 +93,7 @@ export const convertStateToForm = (props: StateToFormParams): AppSettingsFormVal
const formAppSetting = getFormAppSetting(appSettings, slotConfigNames);

return {
site,
site: getCleanedSite(site),
basicPublishingCredentialsPolicies: getFormBasicPublishingCredentialsPolicies(basicPublishingCredentialsPolicies),
config: getCleanedConfig(config),
appSettings: formAppSetting,
Expand All @@ -113,6 +113,23 @@ export const convertStateToForm = (props: StateToFormParams): AppSettingsFormVal
};
};

export const getCleanedSite = (site: ArmObj<Site>) => {
let sshEnabled = site.properties.sshEnabled;
sshEnabled = (isLinuxApp(site) || isWindowsContainer(site)) && sshEnabled === null ? true : sshEnabled;
const functionsRuntimeAdminIsolationEnabled = !!site.properties.functionsRuntimeAdminIsolationEnabled;
const clientCertMode = site.properties.clientCertEnabled ? site.properties.clientCertMode : ClientCertMode.Ignore;

return {
...site,
properties: {
...site.properties,
sshEnabled,
functionsRuntimeAdminIsolationEnabled,
clientCertMode,
},
};
};

export const getCleanedConfig = (config: ArmObj<SiteConfig>) => {
// If Remote Debugging Version is set to VS2015, but Remote Debugging is disabled, just change it to VS2017 to prevent the PUT from failing
const hasRemoteDebuggingDisabledWithVS2015 =
Expand Down Expand Up @@ -174,6 +191,13 @@ export const convertFormToState = (
oldSlotConfigNames: ArmObj<SlotConfigNames>
): ApiSetupReturn => {
const site = { ...values.site };
const { clientCertMode, ClientCertEnabled } = getClientCertValues(
initialValues.site.properties.clientCertMode,
values.site.properties.clientCertMode
);
site.properties.clientCertMode = clientCertMode;
site.properties.clientCertEnabled = ClientCertEnabled;

const slotConfigNames = getStickySettings(values.appSettings, values.connectionStrings, values.azureStorageMounts, oldSlotConfigNames);
const slotConfigNamesModified = isSlotConfigNamesModified(oldSlotConfigNames, slotConfigNames);

Expand Down Expand Up @@ -239,6 +263,15 @@ export const getStorageMountAccessKey = (value: FormAzureStorageMounts) => {
: accessKey;
};

export function getClientCertValues(initialClientCertMode: string, currentClientCertMode: string) {
const isClientCertModeIgnore = currentClientCertMode === ClientCertMode.Ignore;

return {
clientCertMode: isClientCertModeIgnore ? initialClientCertMode : currentClientCertMode,
ClientCertEnabled: !isClientCertModeIgnore,
};
}

export function getStickySettings(
appSettings: FormAppSetting[],
connectionStrings: FormConnectionString[],
Expand Down
Original file line number Diff line number Diff line change
@@ -1,26 +1,20 @@
import React, { useContext, useState } from 'react';
import React, { useContext, useEffect, useState } from 'react';
import { settingsWrapper } from '../../AppSettingsForm';
import { Field, FormikProps } from 'formik';
import RadioButtonNoFormik from '../../../../../components/form-controls/RadioButtonNoFormik';
import { useTranslation } from 'react-i18next';
import { PermissionsContext, SiteContext } from '../../Contexts';
import TextField from '../../../../../components/form-controls/TextField';
import { Stack, PanelType, IChoiceGroupOption } from '@fluentui/react';
import { Stack, PanelType, IChoiceGroupOption, MessageBarType } from '@fluentui/react';
import IconButton from '../../../../../components/IconButton/IconButton';
import EditClientExclusionPaths from './EditClientExclusionPaths';
import { AppSettingsFormValues } from '../../AppSettings.types';
import { ScenarioService } from '../../../../../utils/scenario-checker/scenario.service';
import { ScenarioIds } from '../../../../../utils/scenario-checker/scenario-ids';
import CustomPanel from '../../../../../components/CustomPanel/CustomPanel';
import { ClientCertMode, Site } from '../../../../../models/site/site';
import { ClientCertMode, MinTlsVersion, Site } from '../../../../../models/site/site';
import { ArmObj } from '../../../../../models/arm-obj';

enum CompositeClientCertMode {
Require = 'Require',
Allow = 'Allow',
Optional = 'Optional',
Ignore = 'Ignore',
}
import RadioButton from '../../../../../components/form-controls/RadioButton';
import CustomBanner from '../../../../../components/CustomBanner/CustomBanner';

const ClientCert: React.FC<FormikProps<AppSettingsFormValues>> = props => {
const { values, setFieldValue, initialValues } = props;
Expand All @@ -29,53 +23,18 @@ const ClientCert: React.FC<FormikProps<AppSettingsFormValues>> = props => {
const { app_write, editable, saving } = useContext(PermissionsContext);
const disableAllControls = !app_write || !editable || saving;
const [showPanel, setShowPanel] = useState(false);

const onClientCertModeChange = (e: any, newValue: IChoiceGroupOption) => {
switch (newValue.key) {
case CompositeClientCertMode.Require:
setFieldValue('site.properties.clientCertEnabled', true);
setFieldValue('site.properties.clientCertMode', ClientCertMode.Required);
break;
case CompositeClientCertMode.Allow:
setFieldValue('site.properties.clientCertEnabled', true);
setFieldValue('site.properties.clientCertMode', ClientCertMode.Optional);
break;
case CompositeClientCertMode.Optional:
setFieldValue('site.properties.clientCertEnabled', true);
setFieldValue('site.properties.clientCertMode', ClientCertMode.OptionalInteractiveUser);
break;
case CompositeClientCertMode.Ignore:
setFieldValue('site.properties.clientCertEnabled', false);
break;
default:
setFieldValue('site.properties.clientCertEnabled', false);
break;
}
};

const getCompositeClientCertMode = (siteArm: ArmObj<Site>): CompositeClientCertMode => {
if (siteArm.properties.clientCertEnabled) {
return siteArm.properties.clientCertMode === ClientCertMode.Required
? CompositeClientCertMode.Require
: siteArm.properties.clientCertMode === ClientCertMode.Optional
? CompositeClientCertMode.Allow
: CompositeClientCertMode.Optional;
}

return CompositeClientCertMode.Ignore;
};
const [disableOptionalInteractiveUserOption, setDisableOptionalInteractiveUserOption] = useState(false);
const [clientCertWarningMessage, setClientCertWarningMessage] = useState('');

const getClientCertInfoBubbleMessage = (siteArm: ArmObj<Site>): string => {
const mode = getCompositeClientCertMode(siteArm);

switch (mode) {
case CompositeClientCertMode.Require:
switch (siteArm.properties.clientCertMode) {
case ClientCertMode.Required:
return t('clientCertificateModeRequiredInfoBubbleMessage');
case CompositeClientCertMode.Allow:
return t('clientCertificateModeAllowInfoBubbleMessage');
case CompositeClientCertMode.Optional:
case ClientCertMode.Optional:
return t('clientCertificateModeOptionalInfoBubbleMessage');
case CompositeClientCertMode.Ignore:
case ClientCertMode.OptionalInteractiveUser:
return t('clientCertificateModeOptionalInteractiveUserInfoBubbleMessage');
case ClientCertMode.Ignore:
return t('clientCertificateModeIgnoreInfoBubbleMessage');
default:
return '';
Expand All @@ -84,6 +43,7 @@ const ClientCert: React.FC<FormikProps<AppSettingsFormValues>> = props => {

const scenarioChecker = new ScenarioService(t);
const clientCertEnabled = scenarioChecker.checkScenario(ScenarioIds.incomingClientCertEnabled, { site });

const openClientExclusionPathPanel = () => {
setShowPanel(true);
};
Expand All @@ -95,38 +55,55 @@ const ClientCert: React.FC<FormikProps<AppSettingsFormValues>> = props => {
setShowPanel(false);
};

useEffect(() => {
const http20EnabledOrMinTLSVersion13 =
values.config.properties.http20Enabled || values.config.properties.minTlsVersion === MinTlsVersion.tLS13;
const isClientCertModeOptionalInteractiveUser = values.site.properties.clientCertMode === ClientCertMode.OptionalInteractiveUser;

setDisableOptionalInteractiveUserOption(http20EnabledOrMinTLSVersion13);
setClientCertWarningMessage(http20EnabledOrMinTLSVersion13 ? t('clientCertificateWarningMessage') : '');
if (isClientCertModeOptionalInteractiveUser && http20EnabledOrMinTLSVersion13) {
setFieldValue('site.properties.clientCertMode', ClientCertMode.Ignore);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [values.config.properties.http20Enabled, values.config.properties.minTlsVersion, values.site.properties.clientCertMode]);

return scenarioChecker.checkScenario(ScenarioIds.incomingClientCertSupported, { site }).status !== 'disabled' ? (
<>
<h3>{t('incomingClientCertificates')}</h3>
<div className={settingsWrapper}>
<RadioButtonNoFormik
dirty={getCompositeClientCertMode(values.site) !== getCompositeClientCertMode(initialValues.site)}
{clientCertWarningMessage && (
<CustomBanner id="clinet-cert-warning" message={clientCertWarningMessage} type={MessageBarType.warning} undocked={true} />
)}
<Field
name={'site.properties.clientCertMode'}
component={RadioButton}
dirty={values.site.properties.clientCertMode !== initialValues.site.properties.clientCertMode}
label={t('clientCertificateMode')}
id="incoming-client-certificate-mode"
ariaLabelledBy={`incoming-client-certificate-mode-label`}
disabled={disableAllControls || clientCertEnabled.status === 'disabled' || values.config.properties.http20Enabled}
disabled={disableAllControls || clientCertEnabled.status === 'disabled'}
upsellMessage={clientCertEnabled.status === 'disabled' ? clientCertEnabled.data : ''}
selectedKey={getCompositeClientCertMode(values.site)}
infoBubbleMessage={getClientCertInfoBubbleMessage(values.site)}
options={[
{
key: CompositeClientCertMode.Require,
text: t('clientCertificateModeRequire'),
key: ClientCertMode.Required,
text: t('clientCertificateModeRequired'),
},
{
key: CompositeClientCertMode.Allow,
text: t('clientCertificateModeAllow'),
key: ClientCertMode.Optional,
text: t('clientCertificateModeOptional'),
},
{
key: CompositeClientCertMode.Optional,
text: t('clientCertificateModeOptional'),
key: ClientCertMode.OptionalInteractiveUser,
text: t('clientCertificateModeOptionalInteractiveUser'),
disabled: disableOptionalInteractiveUserOption,
},
{
key: CompositeClientCertMode.Ignore,
key: ClientCertMode.Ignore,
text: t('clientCertificateModeIgnore'),
},
]}
onChange={onClientCertModeChange}
/>
<Stack horizontal>
<Field
Expand All @@ -150,7 +127,7 @@ const ClientCert: React.FC<FormikProps<AppSettingsFormValues>> = props => {
id={`edit-client-cert-exclusion-paths`}
ariaLabel={t('editCertificateExlusionPaths')}
title={t('editCertificateExlusionPaths')}
disabled={disableAllControls || !values.site.properties.clientCertEnabled}
disabled={disableAllControls || values.site.properties.clientCertMode === ClientCertMode.Ignore}
onClick={openClientExclusionPathPanel}
/>
</Stack>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { ScenarioService } from '../../../../utils/scenario-checker/scenario.ser
import { AppSettingsFormValues } from '../AppSettings.types';
import { PermissionsContext, SiteContext } from '../Contexts';
import { Links } from '../../../../utils/FwLinks';
import { IPMode, MinTlsVersion, SslState, VnetPrivatePortsCount } from '../../../../models/site/site';
import { ClientCertMode, IPMode, MinTlsVersion, SslState, VnetPrivatePortsCount } from '../../../../models/site/site';
import CustomBanner from '../../../../components/CustomBanner/CustomBanner';
import { IDropdownOption, MessageBar, MessageBarType, mergeStyles } from '@fluentui/react';
import { CommonConstants, ScmHosts } from '../../../../utils/CommonConstants';
Expand Down Expand Up @@ -111,12 +111,8 @@ const Platform: React.FC<FormikProps<AppSettingsFormValues>> = props => {
[setFieldValue]
);

const onHttp20EnabledChange = (event: React.FormEvent<HTMLDivElement>, option: { key: boolean }) => {
const onHttp20EnabledChange = (_, option: { key: boolean }) => {
props.setFieldValue('config.properties.http20ProxyFlag', 0);
if (option.key) {
props.setFieldValue('site.properties.clientCertEnabled', false);
}

props.setFieldValue('config.properties.http20Enabled', option.key);
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,18 @@ const GeneralSettings: React.FC<FormikProps<AppSettingsFormValues>> = props => {
return null;
};

const isSiteContainer = useMemo(() => {
return values.config?.properties.linuxFxVersion
const showStack = useMemo(() => {
const isSiteContainer = values.config?.properties.linuxFxVersion
? StringUtils.equalsIgnoreCase(values.config?.properties.linuxFxVersion, DeploymentCenterConstants.sitecontainers)
: false;
}, [values.config?.properties.linuxFxVersion]);
const showStackSettingStatus = scenarioChecker.checkScenario(ScenarioIds.showStackSettings, { site }).status;

return !isSiteContainer && showStackSettingStatus !== 'disabled';
}, [values.config?.properties.linuxFxVersion, site]);

return (
<>
{!isSiteContainer && <Stacks {...props} />}
{showStack && <Stacks {...props} />}
{/* NOTE (krmitta): Need to hide platform settings except TLS settings for KubeApp as elements within are not shown */}
<>
<h3>{t('platformSettings')}</h3>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ export const DestinationPlanDetails: React.FC<DestinationPlanDetailsProps> = ({
'P3V3',
'P2V3',
'P1V3',
'P0V3',
'WS1',
'WS2',
'WS3',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ const DeploymentCenterBitbucketConfiguredView: React.FC<DeploymentCenterFieldPro
const getBranchLink = () => {
if (!isBranchInfoMissing) {
return (
<Link key="deployment-center-branch-link" onClick={() => window.open(repoUrl, '_blank')} aria-label={`${branch}`}>
<Link key="deployment-center-branch-link" href={repoUrl} target="_blank" aria-label={`${branch}`}>
{`${branch} `}
<Icon id={`branch-button`} iconName={'NavigateExternalInline'} />
</Link>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,13 +78,13 @@ const DeploymentCenterCodeSettings: React.FC<DeploymentCenterFieldProps<Deployme
const [isVstsSetup, setIsVstsSetup] = useState(false);
const [isTfsOrVsoSetup, setIsTfsOrVsoSetup] = useState(false);
const [isUsingExistingOrAvailableWorkflowConfig, setIsUsingExistingOrAvailableWorkflowConfig] = useState(false);
const [isRemoveEnvEnabled, setIsRemoveEnvEnabled] = useState(false);
const [isRemoveEnvWebAppEnabled, setIsRemoveEnvWebAppEnabled] = useState(false);
useEffect(() => {
let isSubscribed = true;

portalContext?.getBooleanFlight(ExperimentationConstants.FlightVariable.removeDeployEnvironment).then(hasFlightEnabled => {
portalContext?.getBooleanFlight(ExperimentationConstants.FlightVariable.removeDeployEnvironmentWebApp).then(hasFlightEnabled => {
if (isSubscribed) {
setIsRemoveEnvEnabled(hasFlightEnabled);
setIsRemoveEnvWebAppEnabled(hasFlightEnabled);
}
});

Expand Down Expand Up @@ -169,7 +169,7 @@ const DeploymentCenterCodeSettings: React.FC<DeploymentCenterFieldProps<Deployme
variables['javaContainer'] = formProps.values.javaContainer;
}

if (isRemoveEnvEnabled) {
if (isRemoveEnvWebAppEnabled) {
variables['isRemoveEnvEnabled'] = true;
}

Expand Down
Loading
Loading