diff --git a/.changeset/fifty-frogs-prove.md b/.changeset/fifty-frogs-prove.md new file mode 100644 index 0000000000..a3296499cf --- /dev/null +++ b/.changeset/fifty-frogs-prove.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder': patch +--- + +Add options to mask or hide values on review state diff --git a/docs/features/software-templates/writing-templates.md b/docs/features/software-templates/writing-templates.md index 7b475f2fa1..02bb698f64 100644 --- a/docs/features/software-templates/writing-templates.md +++ b/docs/features/software-templates/writing-templates.md @@ -227,6 +227,33 @@ spec: inputType: tel ``` +#### Hide or mask sensitive data on Review step + +Sometimes, specially in custom fields, you collect some data on Create form that +must not be shown to the user on Review step. To hide or mask this data, you can +use `ui:widget: password` or set some properties of `ui:backstage`: + +```yaml +- title: Hide or mask values + properties: + password: + title: Password + type: string + ui:widget: password # will print '******' as value for property 'password' on Review Step + masked: + title: Masked + type: string + ui:backstage: + review: + mask: '' # will print '' as value for property 'Masked' on Review Step + hidden: + title: Hidden + type: string + ui:backstage: + review: + show: false # wont print any info about 'hidden' property on Review Step +``` + #### The Repository Picker So in order to make working with repository providers easier, we've built a diff --git a/plugins/scaffolder/src/components/MultistepJsonForm/MultistepJsonForm.test.tsx b/plugins/scaffolder/src/components/MultistepJsonForm/MultistepJsonForm.test.tsx new file mode 100644 index 0000000000..5f7091832f --- /dev/null +++ b/plugins/scaffolder/src/components/MultistepJsonForm/MultistepJsonForm.test.tsx @@ -0,0 +1,88 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { getReviewData } from './MultistepJsonForm'; + +describe('MultistepJsonForm', () => { + const formDataMock = { + password: 'password', + masked: 'Some info to mask', + open: 'Some open info', + hidden: 'Some info to hide', + 'other-open': 'Other open info', + }; + + const stepsMock = [ + { + title: 'The test template', + schema: { + title: 'The test template', + properties: { + password: { + title: 'Password', + type: 'string', + 'ui:widget': 'password', + }, + masked: { + title: 'Masked', + type: 'string', + 'ui:backstage': { + review: { + show: true, + mask: '******', + }, + }, + }, + open: { + title: 'Open info', + type: 'string', + }, + }, + }, + }, + { + title: 'Other fields', + schema: { + title: 'Other fields', + properties: { + hidden: { + title: 'Hidden', + type: 'string', + 'ui:backstage': { + review: { + show: false, + }, + }, + }, + 'other-open': { + title: 'Other Open Info', + type: 'string', + }, + }, + }, + }, + ]; + + test('Fields are defined to be hidden or masked', () => { + const reviewData = getReviewData(formDataMock, stepsMock); + + expect(reviewData.password).toBe('******'); + expect(reviewData.masked).toBe('******'); + expect(reviewData.open).toBe('Some open info'); + expect(reviewData.hidden).toBeUndefined(); + expect(reviewData['other-open']).toBe('Other open info'); + }); +}); diff --git a/plugins/scaffolder/src/components/MultistepJsonForm/MultistepJsonForm.tsx b/plugins/scaffolder/src/components/MultistepJsonForm/MultistepJsonForm.tsx index 0fc998f82e..cdcc22da29 100644 --- a/plugins/scaffolder/src/components/MultistepJsonForm/MultistepJsonForm.tsx +++ b/plugins/scaffolder/src/components/MultistepJsonForm/MultistepJsonForm.tsx @@ -24,7 +24,7 @@ import { Stepper, Typography, } from '@material-ui/core'; -import { FormProps, IChangeEvent, withTheme } from '@rjsf/core'; +import { FormProps, IChangeEvent, UiSchema, withTheme } from '@rjsf/core'; import { Theme as MuiTheme } from '@rjsf/material-ui'; import React, { useState } from 'react'; import { transformSchemaToProps } from './schema'; @@ -49,6 +49,59 @@ type Props = { fields?: FormProps['fields']; }; +export function getUiSchemasFromSteps(steps: Step[]): UiSchema[] { + const uiSchemas: Array = []; + steps.forEach(step => { + const schemaProps = step.schema.properties as JsonObject; + for (const key in schemaProps) { + if (schemaProps.hasOwnProperty(key)) { + const uiSchema = schemaProps[key] as UiSchema; + uiSchema.name = key; + uiSchemas.push(uiSchema); + } + } + }); + return uiSchemas; +} + +export function getReviewData(formData: Record, steps: Step[]) { + const uiSchemas = getUiSchemasFromSteps(steps); + const reviewData: Record = {}; + for (const key in formData) { + if (formData.hasOwnProperty(key)) { + const uiSchema = uiSchemas.find(us => us.name === key); + + if (!uiSchema) { + reviewData[key] = formData[key]; + continue; + } + + if (uiSchema['ui:widget'] === 'password') { + reviewData[key] = '******'; + continue; + } + + if (!uiSchema['ui:backstage'] || !uiSchema['ui:backstage'].review) { + reviewData[key] = formData[key]; + continue; + } + + const review = uiSchema['ui:backstage'].review as JsonObject; + if (!review.show) { + continue; + } + + if (review.mask) { + reviewData[key] = review.mask; + continue; + } + reviewData[key] = formData[key]; + } + } + + return reviewData; +} + export const MultistepJsonForm = ({ steps, formData, @@ -64,8 +117,9 @@ export const MultistepJsonForm = ({ setActiveStep(0); onReset(); }; - const handleNext = () => + const handleNext = () => { setActiveStep(Math.min(activeStep + 1, steps.length)); + }; const handleBack = () => setActiveStep(Math.max(activeStep - 1, 0)); return ( @@ -113,7 +167,10 @@ export const MultistepJsonForm = ({ Review and create - +