This is an automated email from the ASF dual-hosted git repository. diegopucci pushed a commit to branch diego/ch78628/fix-disabled-ssh-toggle in repository https://gitbox.apache.org/repos/asf/superset.git
commit a17aac711998b1227d72ba7cda36da1fa2dc4912 Author: geido <[email protected]> AuthorDate: Mon Feb 19 17:02:50 2024 +0200 Validate inputs --- .../databases/DatabaseModal/SSHTunnelForm.tsx | 12 ++++----- .../src/features/databases/DatabaseModal/index.tsx | 21 +++++++-------- superset-frontend/src/features/databases/types.ts | 30 +++++++++++++++++----- superset/commands/database/ssh_tunnel/create.py | 3 +++ superset/commands/database/ssh_tunnel/update.py | 9 +++++++ 5 files changed, 49 insertions(+), 26 deletions(-) diff --git a/superset-frontend/src/features/databases/DatabaseModal/SSHTunnelForm.tsx b/superset-frontend/src/features/databases/DatabaseModal/SSHTunnelForm.tsx index 7823d82faf..e0d1b16ff2 100644 --- a/superset-frontend/src/features/databases/DatabaseModal/SSHTunnelForm.tsx +++ b/superset-frontend/src/features/databases/DatabaseModal/SSHTunnelForm.tsx @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -import React, { EventHandler, ChangeEvent, useState } from 'react'; +import React, { useState } from 'react'; import { t, styled } from '@superset-ui/core'; import { AntdForm, Col, Row } from 'src/components'; import { Form, FormLabel } from 'src/components/Form'; @@ -24,7 +24,7 @@ import { Radio } from 'src/components/Radio'; import { Input, TextArea } from 'src/components/Input'; import { Input as AntdInput, Tooltip } from 'antd'; import { EyeInvisibleOutlined, EyeOutlined } from '@ant-design/icons'; -import { DatabaseObject } from '../types'; +import { DatabaseObject, FieldPropTypes } from '../types'; import { AuthType } from '.'; const StyledDiv = styled.div` @@ -54,9 +54,7 @@ const SSHTunnelForm = ({ setSSHTunnelLoginMethod, }: { db: DatabaseObject | null; - onSSHTunnelParametersChange: EventHandler< - ChangeEvent<HTMLInputElement | HTMLTextAreaElement> - >; + onSSHTunnelParametersChange: FieldPropTypes['changeMethods']['onSSHTunnelParametersChange']; setSSHTunnelLoginMethod: (method: AuthType) => void; }) => { const [usePassword, setUsePassword] = useState<AuthType>(AuthType.Password); @@ -86,9 +84,9 @@ const SSHTunnelForm = ({ </FormLabel> <Input name="server_port" - type="text" placeholder={t('22')} - value={db?.ssh_tunnel?.server_port || ''} + type="number" + value={db?.ssh_tunnel?.server_port} onChange={onSSHTunnelParametersChange} data-test="ssh-tunnel-server_port-input" /> diff --git a/superset-frontend/src/features/databases/DatabaseModal/index.tsx b/superset-frontend/src/features/databases/DatabaseModal/index.tsx index a4a7ddad4a..3607e5e400 100644 --- a/superset-frontend/src/features/databases/DatabaseModal/index.tsx +++ b/superset-frontend/src/features/databases/DatabaseModal/index.tsx @@ -662,7 +662,12 @@ const DatabaseModal: FunctionComponent<DatabaseModalProps> = ({ masked_encrypted_extra: db?.masked_encrypted_extra || '', server_cert: db?.server_cert || undefined, ssh_tunnel: - !isEmpty(db?.ssh_tunnel) && useSSHTunneling ? db.ssh_tunnel : undefined, + !isEmpty(db?.ssh_tunnel) && useSSHTunneling + ? { + ...db.ssh_tunnel, + server_port: Number(db.ssh_tunnel!.server_port), + } + : undefined, }; setTestInProgress(true); testDatabaseConnection( @@ -694,10 +699,6 @@ const DatabaseModal: FunctionComponent<DatabaseModalProps> = ({ setValidationErrors(null); }; - const handleClearSSHTunnelConfig = () => { - setDB({ type: ActionType.RemoveSSHTunnelConfig }); - }; - const handleParametersChange = ({ target }: { target: HTMLInputElement }) => { onChange(ActionType.ParametersChange, { type: target.type, @@ -1541,8 +1542,8 @@ const DatabaseModal: FunctionComponent<DatabaseModalProps> = ({ typeof dbErrors === 'object' ? Object.values(dbErrors) : typeof dbErrors === 'string' - ? [dbErrors] - : []; + ? [dbErrors] + : []; } else if ( !isEmpty(validationErrors) && validationErrors?.error_type === 'GENERIC_DB_ENGINE_ERROR' @@ -1578,11 +1579,7 @@ const DatabaseModal: FunctionComponent<DatabaseModalProps> = ({ const renderSSHTunnelForm = () => ( <SSHTunnelForm db={db as DatabaseObject} - onSSHTunnelParametersChange={({ - target, - }: { - target: HTMLInputElement | HTMLTextAreaElement; - }) => + onSSHTunnelParametersChange={({ target }) => onChange(ActionType.ParametersSSHTunnelChange, { type: target.type, name: target.name, diff --git a/superset-frontend/src/features/databases/types.ts b/superset-frontend/src/features/databases/types.ts index 2f481d8315..1ae4a7f715 100644 --- a/superset-frontend/src/features/databases/types.ts +++ b/superset-frontend/src/features/databases/types.ts @@ -1,6 +1,6 @@ import { JsonObject } from '@superset-ui/core'; import { InputProps } from 'antd/lib/input'; -import { FormEvent } from 'react'; +import { ChangeEvent, EventHandler, FormEvent } from 'react'; /** * Licensed to the Apache Software Foundation (ASF) under one @@ -236,23 +236,39 @@ export interface ExtraJson { version?: string; } -type ParametersChangeValueType = Partial<Omit<HTMLInputElement, 'value'>> & { - value?: string | boolean; +type CustomTextType = { + value?: string | boolean | number; + type?: string | null; + name?: string; + checked?: boolean; }; -type ParametersChangeType<T = ParametersChangeValueType> = +type CustomHTMLInputElement = Omit<Partial<CustomTextType>, 'value' | 'type'> & + CustomTextType; + +type CustomHTMLTextAreaElement = Omit< + Partial<CustomTextType>, + 'value' | 'type' +> & + CustomTextType; + +export type CustomParametersChangeType<T = CustomTextType> = | FormEvent<InputProps> | { target: T }; +export type CustomEventHandlerType = EventHandler< + ChangeEvent<CustomHTMLInputElement | CustomHTMLTextAreaElement> +>; + export interface FieldPropTypes { required: boolean; hasTooltip?: boolean; tooltipText?: (value: any) => string; placeholder?: string; - onParametersChange: (event: ParametersChangeType) => void; + onParametersChange: (event: CustomParametersChangeType) => void; onParametersUploadFileChange: (value: any) => string; changeMethods: { - onParametersChange: (event: ParametersChangeType) => void; + onParametersChange: (event: CustomParametersChangeType) => void; } & { onChange: (value: any) => string; } & { @@ -262,7 +278,7 @@ export interface FieldPropTypes { onRemoveTableCatalog: (idx: number) => void; } & { onExtraInputChange: (value: any) => void; - onSSHTunnelParametersChange: (value: any) => string; + onSSHTunnelParametersChange: CustomEventHandlerType; }; validationErrors: JsonObject | null; getValidation: () => void; diff --git a/superset/commands/database/ssh_tunnel/create.py b/superset/commands/database/ssh_tunnel/create.py index cbfee3ce2a..59e083d4d8 100644 --- a/superset/commands/database/ssh_tunnel/create.py +++ b/superset/commands/database/ssh_tunnel/create.py @@ -57,6 +57,7 @@ class CreateSSHTunnelCommand(BaseCommand): server_address: Optional[str] = self._properties.get("server_address") server_port: Optional[int] = self._properties.get("server_port") username: Optional[str] = self._properties.get("username") + password: Optional[str] = self._properties.get("password") private_key: Optional[str] = self._properties.get("private_key") private_key_password: Optional[str] = self._properties.get( "private_key_password" @@ -67,6 +68,8 @@ class CreateSSHTunnelCommand(BaseCommand): exceptions.append(SSHTunnelRequiredFieldValidationError("server_port")) if not username: exceptions.append(SSHTunnelRequiredFieldValidationError("username")) + if not private_key and not password: + exceptions.append(SSHTunnelRequiredFieldValidationError("password")) if private_key_password and private_key is None: exceptions.append(SSHTunnelRequiredFieldValidationError("private_key")) if exceptions: diff --git a/superset/commands/database/ssh_tunnel/update.py b/superset/commands/database/ssh_tunnel/update.py index ae7ee78afe..47f7d4947a 100644 --- a/superset/commands/database/ssh_tunnel/update.py +++ b/superset/commands/database/ssh_tunnel/update.py @@ -43,6 +43,15 @@ class UpdateSSHTunnelCommand(BaseCommand): self.validate() try: if self._model is not None: # So we dont get incompatible types error + # unset password if private key is provided + if self._properties.get("private_key"): + self._properties["password"] = None + + # unset private key and password if password is provided + if self._properties.get("password"): + self._properties["private_key"] = None + self._properties["private_key_password"] = None + tunnel = SSHTunnelDAO.update(self._model, self._properties) except DAOUpdateFailedError as ex: raise SSHTunnelUpdateFailedError() from ex
