Skip to content

Commit

Permalink
[DC] Check on GHA BuildProvider, fix role assignment check, and punyc…
Browse files Browse the repository at this point in the history
…ode non-ASCII characters (#7546)

* [DC] Check on GHA BuildProvider and punycode non-ASCII characters

* Fix styling for combobox error message and change wording on validation error

* Update form validation

* Push update to validation
  • Loading branch information
yoonaoh committed Jan 18, 2024
1 parent 6f567c0 commit 65c135e
Show file tree
Hide file tree
Showing 7 changed files with 84 additions and 46 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { IStyleBaseArray } from '@fluentui/merge-styles';
import { IStyleBaseArray, IRawStyle } from '@fluentui/merge-styles';
import { IDropdownStyles, ITextFieldStyles, ITooltipHostStyles } from '@fluentui/react';
import { style } from 'typestyle';
import { ComboBoxStyles } from '../../theme/CustomOfficeFabric/AzurePortal/ComboBox.styles';
Expand Down Expand Up @@ -42,6 +42,10 @@ export const comboboxStyleOverrides = (theme: ThemeExtended, fullpage: boolean,
width: widthOverride || FORM_DEFAULT_WIDTH,
},
],
errorMessage: {
...(baseStyle.errorMessage as IRawStyle),
width: widthOverride || FORM_DEFAULT_WIDTH,
},
} as IDropdownStyles;
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,10 +153,10 @@ export abstract class DeploymentCenterFormBuilder {
externalRepoType: Yup.mixed().notRequired(),
devOpsProjectName: Yup.mixed().notRequired(),
authType: Yup.mixed().test('authTypeRequired', this._t('deploymentCenterFieldRequiredMessage'), function(value) {
return this.parent.sourceProvider === ScmType.GitHubAction ? !!value : true;
return this.parent.buildProvider === BuildProvider.GitHubAction ? !!value : true;
}),
authIdentity: Yup.mixed().test('authIdentityRequired', this._t('deploymentCenterFieldRequiredMessage'), function(value) {
return this.parent.sourceProvider === ScmType.GitHubAction && this.parent.authType === AuthType.Oidc ? !!value.resourceId : true;
return this.parent.buildProvider === BuildProvider.GitHubAction && this.parent.authType === AuthType.Oidc ? !!value.id : true;
}),
hasPermissionToUseOIDC: Yup.boolean().notRequired(),
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ const DeploymentCenterCodeForm: React.FC<DeploymentCenterCodeFormProps> = props
},
});
} else {
if (values.sourceProvider === ScmType.GitHubAction && values.authType === AuthType.Oidc) {
if (values.buildProvider === BuildProvider.GitHubAction && values.authType === AuthType.Oidc) {
const armId = new ArmResourceDescriptor(deploymentCenterContext.resourceId);
portalContext.log(getTelemetryInfo('info', 'registerManagedIdentityProvider', 'submit'));
const registerManagedIdentityProviderResponse = await deploymentCenterData.registerProvider(
Expand All @@ -86,50 +86,30 @@ const DeploymentCenterCodeForm: React.FC<DeploymentCenterCodeFormProps> = props
return errorResponse;
}

const getRoleAssignmentsWithScope = deploymentCenterData.getRoleAssignmentsWithScope(
deploymentCenterContext.resourceId,
values.authIdentity.properties.principalId
);
const listFederatedCredentials = deploymentCenterData.listFederatedCredentials(values.authIdentity.id);
const [getRoleAssignmentsWithScopeResponse, listFederatedCredentialsResponse] = await Promise.all([
getRoleAssignmentsWithScope,
const [identityHasRole, listFederatedCredentialsResponse] = await Promise.all([
checkRoleAssignmentsForIdentity(values.authIdentity.properties.principalId),
listFederatedCredentials,
]);

if (getRoleAssignmentsWithScopeResponse.metadata.success) {
const hasRoleAssignment = deploymentCenterData.hasRoleAssignment(
if (!identityHasRole && values.hasPermissionToUseOIDC) {
const putWebsiteContributorRoleResponse = await deploymentCenterData.putRoleAssignmentWithScope(
RBACRoleId.websiteContributor,
getRoleAssignmentsWithScopeResponse.data.value
deploymentCenterContext.resourceId,
values.authIdentity.properties.principalId,
PrincipalType.servicePrincipal
);
if (!hasRoleAssignment) {
const putWebsiteContributorRoleResponse = await deploymentCenterData.putRoleAssignmentWithScope(
RBACRoleId.websiteContributor,
deploymentCenterContext.resourceId,
values.authIdentity.properties.principalId,
PrincipalType.servicePrincipal
if (!putWebsiteContributorRoleResponse.metadata.success) {
portalContext.log(
getTelemetryInfo('error', 'putWebsiteContributorRoleResponse', 'failed', {
message: getErrorMessage(putWebsiteContributorRoleResponse.metadata.error),
errorAsString: putWebsiteContributorRoleResponse.metadata.error
? JSON.stringify(putWebsiteContributorRoleResponse.metadata.error)
: '',
})
);
if (!putWebsiteContributorRoleResponse.metadata.success) {
portalContext.log(
getTelemetryInfo('error', 'putWebsiteContributorRoleResponse', 'failed', {
message: getErrorMessage(putWebsiteContributorRoleResponse.metadata.error),
errorAsString: putWebsiteContributorRoleResponse.metadata.error
? JSON.stringify(putWebsiteContributorRoleResponse.metadata.error)
: '',
})
);
return putWebsiteContributorRoleResponse;
}
return putWebsiteContributorRoleResponse;
}
} else {
portalContext.log(
getTelemetryInfo('error', 'getRoleAssignmentsWithScopeResponse', 'failed', {
message: getErrorMessage(getRoleAssignmentsWithScopeResponse.metadata.error),
errorAsString: getRoleAssignmentsWithScopeResponse.metadata.error
? JSON.stringify(getRoleAssignmentsWithScopeResponse.metadata.error)
: '',
})
);
return getRoleAssignmentsWithScopeResponse;
}

if (listFederatedCredentialsResponse.metadata.success) {
Expand Down Expand Up @@ -287,6 +267,49 @@ const DeploymentCenterCodeForm: React.FC<DeploymentCenterCodeFormProps> = props
}
};

const checkRoleAssignmentsForIdentity = async (principalId?: string) => {
if (principalId) {
const armId = new ArmResourceDescriptor(deploymentCenterContext.resourceId);
const subscriptionId = `/subscriptions/${armId.subscription}`;
const resourceGroupId = `/subscriptions/${armId.subscription}/resourceGroups/${armId.resourceGroup}`;
const [roleAssignmentsOnSub, roleAssignmentsOnRg, roleAssignmentsOnApp] = await Promise.all([
deploymentCenterData.getRoleAssignmentsWithScope(subscriptionId, principalId),
deploymentCenterData.getRoleAssignmentsWithScope(resourceGroupId, principalId),
deploymentCenterData.getRoleAssignmentsWithScope(deploymentCenterContext.resourceId, principalId),
]);

if (roleAssignmentsOnSub.metadata.success && roleAssignmentsOnRg.metadata.success && roleAssignmentsOnApp.metadata.success) {
const hasOwnerAccess = deploymentCenterData.hasRoleAssignment(RBACRoleId.owner, [
...roleAssignmentsOnSub.data.value,
...roleAssignmentsOnRg.data.value,
...roleAssignmentsOnApp.data.value,
]);
const hasContributorAccess = deploymentCenterData.hasRoleAssignment(RBACRoleId.contributor, [
...roleAssignmentsOnSub.data.value,
...roleAssignmentsOnRg.data.value,
...roleAssignmentsOnApp.data.value,
]);
const hasWebsiteContributorAccess = deploymentCenterData.hasRoleAssignment(RBACRoleId.websiteContributor, [
...roleAssignmentsOnSub.data.value,
...roleAssignmentsOnRg.data.value,
...roleAssignmentsOnApp.data.value,
]);

return hasOwnerAccess || hasContributorAccess || hasWebsiteContributorAccess;
} else {
portalContext.log(
getTelemetryInfo('error', 'checkRoleAssignmentsForIdentityForOidc', 'failed', {
message:
`roleAssignmentsOnSub: ${getErrorMessage(roleAssignmentsOnSub.metadata.error)}, ` +
`roleAssignmentsOnRg: ${getErrorMessage(roleAssignmentsOnRg.metadata.error)}, ` +
`roleAssignmentsOnApp: ${getErrorMessage(roleAssignmentsOnApp.metadata.error)} `,
})
);
}
}
return false;
};

const getKuduSourceControlsPayload = (values: DeploymentCenterFormData<DeploymentCenterCodeFormData>): SiteSourceControlRequestBody => {
return {
repoUrl: getRepoUrl(values),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,7 @@ const DeploymentCenterCodeSettings: React.FC<DeploymentCenterFieldProps<Deployme
useEffect(() => {
if (
deploymentCenterContext.siteDescriptor &&
formProps.values.branch &&
(formProps.values.workflowOption === WorkflowOption.UseExistingWorkflowConfig ||
formProps.values.workflowOption === WorkflowOption.Add ||
formProps.values.workflowOption === WorkflowOption.Overwrite)
Expand All @@ -248,7 +249,7 @@ const DeploymentCenterCodeSettings: React.FC<DeploymentCenterFieldProps<Deployme
setWorkflowFilePath('');
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [formProps.values.workflowOption]);
}, [deploymentCenterContext.siteDescriptor, formProps.values.branch, formProps.values.workflowOption]);

const getSettingsControls = () => (
<>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,14 @@ const DeploymentCenterCodeSourceAndBuild: React.FC<DeploymentCenterFieldProps<De
);
}, [selectedBuild, deploymentCenterPublishingContext.basicPublishingCredentialsPolicies?.scm.allow, formProps.values.authType]);

useEffect(() => {
if (selectedBuild === BuildProvider.GitHubAction) {
formProps.setFieldValue('authType', AuthType.Oidc);
} else {
formProps.setFieldValue('authType', AuthType.PublishProfile);
}
}, [selectedBuild]);

const showNoWritePermissionBanner = useMemo(() => {
return !deploymentCenterContext.hasWritePermission;
}, [deploymentCenterContext.hasWritePermission]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import { ISiteState } from '../../../../SiteState';
import { Guid } from '../../../../utils/Guid';
import { truncate } from 'lodash-es';
import { isSameLocation } from '../../../../utils/location';
import { toASCII } from 'punycode';

export const getRuntimeStackSetting = (
isLinuxApp: boolean,
Expand Down Expand Up @@ -614,11 +615,12 @@ export const getFederatedCredentialName = (fullRepoName: string): string => {

export const getUserAssignedIdentityName = (appName: string): string => {
const guid = Guid.newTinyGuid();
if (`${appName}-id-${guid}`.length > 24) {
return `${truncate(appName, { length: 24 - `-id-${guid}`.length, omission: '' })}-id-${guid}`;
const encodedAppName = toASCII(appName);
if (`${encodedAppName}-id-${guid}`.length > 24) {
return `${truncate(encodedAppName, { length: 24 - `-id-${guid}`.length, omission: '' })}-id-${guid}`;
}

return `${appName}-id-${guid}`;
return `${encodedAppName}-id-${guid}`;
};

export const isScmTypeValidForContainers = (scmType: ScmType): boolean => {
Expand Down
4 changes: 2 additions & 2 deletions server/Resources/Resources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -7895,10 +7895,10 @@ Set to "External URL" to use an API definition that is hosted elsewhere.</value>
<value>You do not have sufficient permissions to authenticate with user-assigned identity.</value>
</data>
<data name="authenticationSettingsIdentityAssignmentPermissionsError" xml:space="preserve">
<value>You do not have sufficient permissions to assign role-based access to a managed identity within this resource group and configure federated credentials. If you wish to continue, please select an existing identity below. The identity must have write permissions on the app.</value>
<value>You do not have sufficient permissions to assign role-based access to a managed identity within this resource group and configure federated credentials. If you wish to continue, please select an existing identity below. The identity must have a Website Contributor or higher role on the app.</value>
</data>
<data name="authenticationSettingsIdentityWritePermissionsError" xml:space="preserve">
<value>This identity does not have write permissions on this app. Please select a different identity, or work with your admin to grant the Website Contributor role to your identity on this app.</value>
<value>This identity does not have a Website Contributor or higher role on this app. Please select a different identity, or work with your admin to grant the Website Contributor role to your identity on this app.</value>
</data>
<data name="authenticationSettingsIdentityUnsupportedRegionError" xml:space="preserve">
<value>This identity is in a region that does not support creating federated identity credentials. Please select an identity from a different region.</value>
Expand Down

0 comments on commit 65c135e

Please sign in to comment.