Skip to content

Commit acb7f93

Browse files
#RI-4481 - hide security fields (#2647)
* #RI-4481 - hide security fields
1 parent 92db72b commit acb7f93

File tree

15 files changed

+341
-71
lines changed

15 files changed

+341
-71
lines changed

redisinsight/ui/src/constants/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,5 @@ export * from './workbench'
2828
export * from './featureFlags'
2929
export * from './serverVersions'
3030
export * from './customErrorCodes'
31+
export * from './securityField'
3132
export { ApiEndpoints, BrowserStorageItem, ApiStatusCode, apiErrors }
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const SECURITY_FIELD = '************'

redisinsight/ui/src/pages/home/components/AddInstanceForm/InstanceForm/InstanceForm.spec.tsx

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { BuildType } from 'uiSrc/constants/env'
66
import { appRedirectionSelector } from 'uiSrc/slices/app/url-handling'
77
import { UrlHandlingActions } from 'uiSrc/slices/interfaces/urlHandling'
88
import InstanceForm, { Props } from './InstanceForm'
9-
import { ADD_NEW_CA_CERT } from './constants'
9+
import { ADD_NEW_CA_CERT, SshPassType } from './constants'
1010
import { DbConnectionInfo } from './interfaces'
1111

1212
const BTN_SUBMIT = 'btn-submit'
@@ -1138,6 +1138,55 @@ describe('InstanceForm', () => {
11381138
expect(screen.getByTestId('password')).toHaveAttribute('maxLength', '10000')
11391139
})
11401140

1141+
it('should render security fields with proper attributes', () => {
1142+
render(
1143+
<InstanceForm
1144+
{...instance(mockedProps)}
1145+
formFields={{
1146+
...formFields,
1147+
connectionType: ConnectionType.Standalone,
1148+
ssh: true,
1149+
password: true,
1150+
sshOptions: { host: 'host', port: 123, passphrase: true },
1151+
sshPassType: SshPassType.PrivateKey,
1152+
}}
1153+
/>
1154+
)
1155+
1156+
expect(screen.getByTestId('password')).toHaveAttribute('value', '************')
1157+
expect(screen.getByTestId('password')).toHaveAttribute('type', 'password')
1158+
expect(screen.getByTestId('sshPassphrase')).toHaveAttribute('value', '************')
1159+
expect(screen.getByTestId('sshPassphrase')).toHaveAttribute('type', 'password')
1160+
1161+
fireEvent.focus(screen.getByTestId('password'))
1162+
fireEvent.focus(screen.getByTestId('sshPassphrase'))
1163+
1164+
expect(screen.getByTestId('password')).toHaveAttribute('value', '')
1165+
expect(screen.getByTestId('sshPassphrase')).toHaveAttribute('value', '')
1166+
})
1167+
1168+
it('should render ssh password with proper attributes', () => {
1169+
render(
1170+
<InstanceForm
1171+
{...instance(mockedProps)}
1172+
formFields={{
1173+
...formFields,
1174+
connectionType: ConnectionType.Standalone,
1175+
ssh: true,
1176+
sshOptions: { host: 'host', port: 123, password: true },
1177+
sshPassType: SshPassType.Password,
1178+
}}
1179+
/>
1180+
)
1181+
1182+
expect(screen.getByTestId('sshPassword')).toHaveAttribute('value', '************')
1183+
expect(screen.getByTestId('sshPassword')).toHaveAttribute('type', 'password')
1184+
1185+
fireEvent.focus(screen.getByTestId('sshPassword'))
1186+
1187+
expect(screen.getByTestId('sshPassword')).toHaveAttribute('value', '')
1188+
})
1189+
11411190
it('should render ssh password input with 10_000 length limit', () => {
11421191
render(
11431192
<InstanceForm

redisinsight/ui/src/pages/home/components/AddInstanceForm/InstanceForm/form-components/DatabaseForm.tsx

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
EuiToolTip
1313
} from '@elastic/eui'
1414
import { BuildType } from 'uiSrc/constants/env'
15+
import { SECURITY_FIELD } from 'uiSrc/constants'
1516
import { appInfoSelector } from 'uiSrc/slices/app/info'
1617
import { handlePasteHostName, MAX_PORT_NUMBER, MAX_TIMEOUT_NUMBER, selectOnFocus, validateField, validatePortNumber, validateTimeoutNumber } from 'uiSrc/utils'
1718
import { ConnectionType, InstanceType } from 'uiSrc/slices/interfaces'
@@ -178,16 +179,24 @@ const DatabaseForm = (props: Props) => {
178179
<EuiFlexItem className={flexItemClassName}>
179180
<EuiFormRow label="Password">
180181
<EuiFieldPassword
181-
type="dual"
182+
type={instanceType !== InstanceType.Sentinel ? 'password' : 'dual'}
182183
name="password"
183184
id="password"
184185
data-testid="password"
185186
fullWidth
186187
className="passwordField"
187188
maxLength={10_000}
188189
placeholder="Enter Password"
189-
value={formik.values.password ?? ''}
190+
value={formik.values.password === true ? SECURITY_FIELD : formik.values.password ?? ''}
190191
onChange={formik.handleChange}
192+
onFocus={() => {
193+
if (formik.values.password === true) {
194+
formik.setFieldValue(
195+
'password',
196+
'',
197+
)
198+
}
199+
}}
191200
dualToggleProps={{ color: 'text' }}
192201
autoComplete="new-password"
193202
/>

redisinsight/ui/src/pages/home/components/AddInstanceForm/InstanceForm/form-components/SSHDetails.tsx

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import {
2121
validateField,
2222
validatePortNumber
2323
} from 'uiSrc/utils'
24+
import { SECURITY_FIELD } from 'uiSrc/constants'
2425

2526
import { SshPassType } from '../constants'
2627
import { DbConnectionInfo } from '../interfaces'
@@ -164,17 +165,24 @@ const SSHDetails = (props: Props) => {
164165
<EuiFlexItem className={flexItemClassName}>
165166
<EuiFormRow label="Password">
166167
<EuiFieldPassword
167-
type="dual"
168+
type="password"
168169
name="sshPassword"
169170
id="sshPassword"
170171
data-testid="sshPassword"
171172
fullWidth
172173
className="passwordField"
173174
maxLength={10_000}
174175
placeholder="Enter SSH Password"
175-
value={formik.values.sshPassword ?? ''}
176+
value={formik.values.sshPassword === true ? SECURITY_FIELD : formik.values.sshPassword ?? ''}
176177
onChange={formik.handleChange}
177-
dualToggleProps={{ color: 'text' }}
178+
onFocus={() => {
179+
if (formik.values.sshPassword === true) {
180+
formik.setFieldValue(
181+
'sshPassword',
182+
'',
183+
)
184+
}
185+
}}
178186
autoComplete="new-password"
179187
/>
180188
</EuiFormRow>
@@ -209,17 +217,24 @@ const SSHDetails = (props: Props) => {
209217
<EuiFlexItem className={flexItemClassName}>
210218
<EuiFormRow label="Passphrase">
211219
<EuiFieldPassword
212-
type="dual"
220+
type="password"
213221
name="sshPassphrase"
214222
id="sshPassphrase"
215223
data-testid="sshPassphrase"
216224
fullWidth
217225
className="passwordField"
218226
maxLength={50_000}
219227
placeholder="Enter Passphrase for Private Key"
220-
value={formik.values.sshPassphrase ?? ''}
228+
value={formik.values.sshPassphrase === true ? SECURITY_FIELD : formik.values.sshPassphrase ?? ''}
221229
onChange={formik.handleChange}
222-
dualToggleProps={{ color: 'text' }}
230+
onFocus={() => {
231+
if (formik.values.sshPassphrase === true) {
232+
formik.setFieldValue(
233+
'sshPassphrase',
234+
'',
235+
)
236+
}
237+
}}
223238
autoComplete="new-password"
224239
/>
225240
</EuiFormRow>

redisinsight/ui/src/pages/home/components/AddInstanceForm/InstanceForm/interfaces.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ export interface DbConnectionInfo extends Instance {
1616
newCaCertName?: string
1717
newCaCert?: string
1818
username?: string
19-
password?: string
19+
password?: string | true
2020
timeout?: string
2121
showDb?: boolean
2222
showCompressor?: boolean
@@ -29,9 +29,9 @@ export interface DbConnectionInfo extends Instance {
2929
sshHost: string
3030
sshPort: string
3131
sshUsername?: string
32-
sshPassword?: string
32+
sshPassword?: string | true
3333
sshPrivateKey?: string
34-
sshPassphrase?: string
34+
sshPassphrase?: string | true
3535
}
3636

3737
export interface ISubmitButton {

redisinsight/ui/src/pages/home/components/AddInstanceForm/InstanceFormWrapper.tsx

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/* eslint-disable no-nested-ternary */
22
import { ConnectionString } from 'connection-string'
3-
import { isUndefined, pick, toNumber, toString } from 'lodash'
3+
import { isUndefined, pick, toNumber, toString, omit } from 'lodash'
44
import React, { useEffect, useState } from 'react'
55
import { useDispatch, useSelector } from 'react-redux'
66
import { useHistory } from 'react-router'
@@ -11,14 +11,14 @@ import {
1111
instancesSelector,
1212
testInstanceStandaloneAction,
1313
updateInstanceAction,
14+
cloneInstanceAction,
1415
} from 'uiSrc/slices/instances/instances'
1516
import { fetchMastersSentinelAction, sentinelSelector, } from 'uiSrc/slices/instances/sentinel'
16-
import { Nullable, removeEmpty, transformQueryParamsObject } from 'uiSrc/utils'
17-
import { localStorageService } from 'uiSrc/services'
17+
import { Nullable, removeEmpty, getFormUpdates, transformQueryParamsObject } from 'uiSrc/utils'
1818
import { sendEventTelemetry, TelemetryEvent } from 'uiSrc/telemetry'
1919
import { caCertsSelector, fetchCaCerts } from 'uiSrc/slices/instances/caCerts'
2020
import { ConnectionType, Instance, InstanceType, } from 'uiSrc/slices/interfaces'
21-
import { BrowserStorageItem, DbType, Pages, REDIS_URI_SCHEMES } from 'uiSrc/constants'
21+
import { DbType, Pages, REDIS_URI_SCHEMES } from 'uiSrc/constants'
2222
import { clientCertsSelector, fetchClientCerts, } from 'uiSrc/slices/instances/clientCerts'
2323
import { appInfoSelector } from 'uiSrc/slices/app/info'
2424

@@ -192,6 +192,10 @@ const InstanceFormWrapper = (props: Props) => {
192192
dispatch(updateInstanceAction(payload, onDbEdited))
193193
}
194194

195+
const handleCloneDatabase = (payload: any) => {
196+
dispatch(cloneInstanceAction(payload))
197+
}
198+
195199
const handleUpdateEditingName = (name: string) => {
196200
const requiredFields = [
197201
'id',
@@ -294,7 +298,14 @@ const InstanceFormWrapper = (props: Props) => {
294298
}
295299
}
296300

297-
dispatch(testInstanceStandaloneAction(removeEmpty(database)))
301+
if (editMode && editedInstance) {
302+
dispatch(testInstanceStandaloneAction({
303+
...getFormUpdates(database, editedInstance),
304+
id: editedInstance.id,
305+
}))
306+
} else {
307+
dispatch(testInstanceStandaloneAction(removeEmpty(database)))
308+
}
298309
}
299310

300311
const autoFillFormDetails = (content: string): boolean => {
@@ -323,7 +334,7 @@ const InstanceFormWrapper = (props: Props) => {
323334
host: details.hostname || host || 'localhost',
324335
port: `${details.port || port || 9443}`,
325336
username: details.user || '',
326-
password: details.password || '',
337+
password: details.password,
327338
tls: details.protocol === 'rediss',
328339
ssh: false,
329340
sshPassType: SshPassType.Password
@@ -405,7 +416,7 @@ const InstanceFormWrapper = (props: Props) => {
405416
}
406417
}
407418

408-
const editDatabase = (tlsSettings: any, values: DbConnectionInfo) => {
419+
const editDatabase = (tlsSettings: any, values: DbConnectionInfo, isCloneMode: boolean) => {
409420
const {
410421
name,
411422
host,
@@ -442,7 +453,14 @@ const InstanceFormWrapper = (props: Props) => {
442453
database.sentinelMaster.password = sentinelMasterPassword
443454
}
444455

445-
handleEditDatabase(removeEmpty(database))
456+
if (isCloneMode) {
457+
// name need for success message
458+
const payload = getFormUpdates(database, omit(editedInstance, ['id', 'name']))
459+
handleCloneDatabase(payload)
460+
} else {
461+
const payload = getFormUpdates(database, omit(editedInstance, 'id'))
462+
handleEditDatabase(payload)
463+
}
446464
}
447465

448466
const addDatabase = (tlsSettings: any, values: DbConnectionInfo) => {
@@ -536,8 +554,8 @@ const InstanceFormWrapper = (props: Props) => {
536554
: undefined,
537555
}
538556

539-
if (editMode && !isCloneMode) {
540-
editDatabase(tlsSettings, values)
557+
if (editMode) {
558+
editDatabase(tlsSettings, values, isCloneMode)
541559
} else {
542560
addDatabase(tlsSettings, values)
543561
}

0 commit comments

Comments
 (0)