mirror of
https://github.com/logto-io/logto.git
synced 2024-12-30 20:33:54 -05:00
feat(console): should mask confidential connector config values by default (#3645)
This commit is contained in:
parent
e4fc54000e
commit
6bb4098409
4 changed files with 50 additions and 5 deletions
|
@ -6,6 +6,12 @@
|
|||
display: none;
|
||||
}
|
||||
|
||||
.hideTextContainerContent {
|
||||
input {
|
||||
-webkit-text-security: disc;
|
||||
}
|
||||
}
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
|
|
@ -9,21 +9,52 @@ import {
|
|||
forwardRef,
|
||||
type Ref,
|
||||
useEffect,
|
||||
useState,
|
||||
} from 'react';
|
||||
|
||||
import EyeClosed from '@/assets/images/eye-closed.svg';
|
||||
import Eye from '@/assets/images/eye.svg';
|
||||
import IconButton from '@/components/IconButton';
|
||||
|
||||
import * as styles from './index.module.scss';
|
||||
|
||||
type Props = Omit<HTMLProps<HTMLInputElement>, 'size'> & {
|
||||
error?: string | boolean;
|
||||
icon?: ReactElement;
|
||||
suffix?: ReactElement;
|
||||
isConfidential?: boolean;
|
||||
};
|
||||
|
||||
function TextInput(
|
||||
{ error, icon, suffix, disabled, className, readOnly, type = 'text', ...rest }: Props,
|
||||
{
|
||||
error,
|
||||
icon,
|
||||
suffix,
|
||||
disabled,
|
||||
className,
|
||||
readOnly,
|
||||
type = 'text',
|
||||
isConfidential = false,
|
||||
...rest
|
||||
}: Props,
|
||||
reference: Ref<Nullable<HTMLInputElement>>
|
||||
) {
|
||||
const innerRef = useRef<HTMLInputElement>(null);
|
||||
const [isContentHidden, setIsContentHidden] = useState(true);
|
||||
|
||||
const toggleHiddenContent = () => {
|
||||
setIsContentHidden((previous) => !previous);
|
||||
};
|
||||
|
||||
const suffixIcon =
|
||||
isConfidential && type === 'text' ? (
|
||||
<IconButton onClick={toggleHiddenContent}>
|
||||
{isContentHidden ? <EyeClosed /> : <Eye />}
|
||||
</IconButton>
|
||||
) : (
|
||||
suffix
|
||||
);
|
||||
|
||||
useImperativeHandle(reference, () => innerRef.current);
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -54,6 +85,7 @@ function TextInput(
|
|||
className={classNames(
|
||||
styles.container,
|
||||
error && styles.error,
|
||||
isConfidential && isContentHidden && type === 'text' && styles.hideTextContainerContent,
|
||||
icon && styles.withIcon,
|
||||
disabled && styles.disabled,
|
||||
readOnly && styles.readOnly
|
||||
|
@ -61,9 +93,9 @@ function TextInput(
|
|||
>
|
||||
{icon && <span className={styles.icon}>{icon}</span>}
|
||||
<input type={type} {...rest} ref={innerRef} disabled={disabled} readOnly={readOnly} />
|
||||
{suffix &&
|
||||
cloneElement(suffix, {
|
||||
className: classNames([suffix.props.className, styles.suffix]),
|
||||
{suffixIcon &&
|
||||
cloneElement(suffixIcon, {
|
||||
className: classNames([suffixIcon.props.className, styles.suffix]),
|
||||
})}
|
||||
</div>
|
||||
{Boolean(error) && typeof error === 'string' && (
|
||||
|
|
|
@ -60,7 +60,13 @@ function ConfigForm({ formItems }: Props) {
|
|||
});
|
||||
|
||||
if (item.type === ConnectorConfigFormItemType.Text) {
|
||||
return <TextInput {...buildCommonProperties()} />;
|
||||
return (
|
||||
<TextInput
|
||||
{...buildCommonProperties()}
|
||||
// TODO: update connectors form config and remove RegExp check
|
||||
isConfidential={item.isConfidential ?? /(Key|Secret)$/.test(item.key)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (item.type === ConnectorConfigFormItemType.MultilineText) {
|
||||
|
|
|
@ -114,6 +114,7 @@ const baseConfigFormItem = {
|
|||
.optional(),
|
||||
description: z.string().optional(),
|
||||
tooltip: z.string().optional(),
|
||||
isConfidential: z.boolean().optional(), // For `Text` type only.
|
||||
};
|
||||
|
||||
const connectorConfigFormItemGuard = z.discriminatedUnion('type', [
|
||||
|
|
Loading…
Reference in a new issue