From 3653db3a6a113e73075687ad35b5d15e8dd74132 Mon Sep 17 00:00:00 2001
From: Jambaldorj Ochirpurev
Date: Fri, 3 Mar 2023 06:20:53 +0100
Subject: [PATCH] Add the Client-side Input Validators for Stream Keys and the
Admin Password (#2619)
* add the minimum stream key complexity rules on the client side
* add an admin password validator
* merge TextField and TextFieldAdmin components
* update Input Validators for Streak Keys and Admin Password
* fix a small regex typo
* code cleanup
* update Textfield and TextFieldWithSubmit
* Prettified Code!
* update the TextFieldWithSubmit component
* correct the admin password endpoind API
* refactor the Admin Password Input field and add a new boolean field for it
* refactor the Form Input field name from adminPassword to InputFieldPassword
* put password regex rules into config-constants.tsx
* regex constant typo fix
* change the boolean variable isAdminPwdField to hasComplexityRequirements
* fix a merge conflict
* Prettified Code!
---------
Co-authored-by: dorj222
---
web/components/admin/TextField.tsx | 109 ++++++++++++++----
web/components/admin/TextFieldWithSubmit.tsx | 26 +++--
.../admin/config/server/StreamKeys.tsx | 59 ++++++++--
web/utils/config-constants.tsx | 27 +++++
4 files changed, 181 insertions(+), 40 deletions(-)
diff --git a/web/components/admin/TextField.tsx b/web/components/admin/TextField.tsx
index cc9b37721..db772f318 100644
--- a/web/components/admin/TextField.tsx
+++ b/web/components/admin/TextField.tsx
@@ -1,10 +1,11 @@
-import React, { FC } from 'react';
+import React, { FC, useEffect, useState } from 'react';
import classNames from 'classnames';
-import { Input, InputNumber } from 'antd';
+import { Input, Form, InputNumber, Button } from 'antd';
import { FieldUpdaterFunc } from '../../types/config-section';
// import InfoTip from '../info-tip';
import { StatusState } from '../../utils/input-statuses';
import { FormStatusIndicator } from './FormStatusIndicator';
+import { PASSWORD_COMPLEXITY_RULES, REGEX_PASSWORD } from '../../utils/config-constants';
export const TEXTFIELD_TYPE_TEXT = 'default';
export const TEXTFIELD_TYPE_PASSWORD = 'password'; // Input.Password
@@ -17,7 +18,7 @@ export type TextFieldProps = {
onSubmit?: () => void;
onPressEnter?: () => void;
-
+ onHandleSubmit?: () => void;
className?: string;
disabled?: boolean;
label?: string;
@@ -31,6 +32,7 @@ export type TextFieldProps = {
useTrim?: boolean;
useTrimLead?: boolean;
value?: string | number;
+ hasComplexityRequirements?: boolean;
onBlur?: FieldUpdaterFunc;
onChange?: FieldUpdaterFunc;
};
@@ -44,6 +46,7 @@ export const TextField: FC = ({
onBlur,
onChange,
onPressEnter,
+ onHandleSubmit,
pattern,
placeholder,
required,
@@ -52,15 +55,30 @@ export const TextField: FC = ({
type,
useTrim,
value,
+ hasComplexityRequirements,
}) => {
+ const [hasPwdChanged, setHasPwdChanged] = useState(false);
+ const [showPwdButton, setShowPwdButton] = useState(false);
+ const [form] = Form.useForm();
const handleChange = (e: any) => {
// if an extra onChange handler was sent in as a prop, let's run that too.
if (onChange) {
const val = type === TEXTFIELD_TYPE_NUMBER ? e : e.target.value;
+ setShowPwdButton(true);
+ if (hasComplexityRequirements && REGEX_PASSWORD.test(val)) {
+ setHasPwdChanged(true);
+ } else {
+ setHasPwdChanged(false);
+ }
+
onChange({ fieldName, value: useTrim ? val.trim() : val });
}
};
+ useEffect(() => {
+ form.setFieldsValue({ inputFieldPassword: value });
+ }, [value]);
+
// if you blur a required field with an empty value, restore its original value in state (parent's state), if an onChange from parent is available.
const handleBlur = (e: any) => {
const val = e.target.value;
@@ -74,7 +92,8 @@ export const TextField: FC = ({
onPressEnter();
}
};
-
+ // Password Complexity rules
+ const passwordComplexityRules = [];
// display the appropriate Ant text field
let Field = Input as
| typeof Input
@@ -88,6 +107,9 @@ export const TextField: FC = ({
autoSize: true,
};
} else if (type === TEXTFIELD_TYPE_PASSWORD) {
+ PASSWORD_COMPLEXITY_RULES.forEach(element => {
+ passwordComplexityRules.push(element);
+ });
Field = Input.Password;
fieldProps = {
visibilityToggle: true,
@@ -128,25 +150,66 @@ export const TextField: FC = ({
) : null}
-
-
-
+ {!hasComplexityRequirements ? (
+
-
-
{tip}
-
+ ) : (
+
+
+
+
+
+ {showPwdButton && (
+
+
+
+ )}
+
+
{tip}
+
+
+
+ )}
);
};
@@ -168,9 +231,11 @@ TextField.defaultProps = {
pattern: '',
useTrim: false,
useTrimLead: false,
+ hasComplexityRequirements: false,
onSubmit: () => {},
onBlur: () => {},
onChange: () => {},
onPressEnter: () => {},
+ onHandleSubmit: () => {},
};
diff --git a/web/components/admin/TextFieldWithSubmit.tsx b/web/components/admin/TextFieldWithSubmit.tsx
index ce89c7030..facc2ebae 100644
--- a/web/components/admin/TextFieldWithSubmit.tsx
+++ b/web/components/admin/TextFieldWithSubmit.tsx
@@ -24,6 +24,7 @@ export type TextFieldWithSubmitProps = TextFieldProps & {
apiPath: string;
configPath?: string;
initialValue?: string;
+ hasComplexityRequirements?: boolean;
};
export const TextFieldWithSubmit: FC = ({
@@ -43,7 +44,8 @@ export const TextFieldWithSubmit: FC = ({
let resetTimer = null;
- const { fieldName, required, tip, status, value, onChange, onSubmit } = textFieldProps;
+ const { fieldName, required, tip, status, value, hasComplexityRequirements, onChange, onSubmit } =
+ textFieldProps;
// Clear out any validation states and messaging
const resetStates = () => {
@@ -118,6 +120,7 @@ export const TextFieldWithSubmit: FC = ({
'textfield-with-submit-container': true,
submittable: hasChanged,
});
+
return (
@@ -126,6 +129,7 @@ export const TextFieldWithSubmit: FC = ({
onSubmit={null}
onBlur={handleBlur}
onChange={handleChange}
+ onHandleSubmit={handleSubmit}
/>
@@ -134,15 +138,17 @@ export const TextFieldWithSubmit: FC
= ({
{tip}
-
+ {!hasComplexityRequirements && (
+
+ )}
diff --git a/web/components/admin/config/server/StreamKeys.tsx b/web/components/admin/config/server/StreamKeys.tsx
index 24685c777..2ca850215 100644
--- a/web/components/admin/config/server/StreamKeys.tsx
+++ b/web/components/admin/config/server/StreamKeys.tsx
@@ -1,12 +1,12 @@
-import React, { useContext, useState } from 'react';
+import React, { useContext, useEffect, useState } from 'react';
import { Table, Space, Button, Typography, Alert, Input, Form } from 'antd';
import dynamic from 'next/dynamic';
import { ServerStatusContext } from '../../../../utils/server-status-context';
import { fetchData, UPDATE_STREAM_KEYS } from '../../../../utils/apis';
+import { PASSWORD_COMPLEXITY_RULES, REGEX_PASSWORD } from '../../../../utils/config-constants';
const { Paragraph } = Typography;
-const { Item } = Form;
// Lazy loaded components
@@ -54,6 +54,18 @@ const generateRndKey = () => {
};
const AddKeyForm = ({ setShowAddKeyForm, setFieldInConfigState, streamKeys, setError }) => {
+ const [hasChanged, setHasChanged] = useState(true);
+ const [form] = Form.useForm();
+ const { Item } = Form;
+ // Password Complexity rules
+ const passwordComplexityRules = [];
+
+ useEffect(() => {
+ PASSWORD_COMPLEXITY_RULES.forEach(element => {
+ passwordComplexityRules.push(element);
+ });
+ }, []);
+
const handleAddKey = (newkey: any) => {
const updatedKeys = [...streamKeys, newkey];
@@ -67,19 +79,50 @@ const AddKeyForm = ({ setShowAddKeyForm, setFieldInConfigState, streamKeys, setE
setShowAddKeyForm(false);
};
+ const handleInputChange = (event: any) => {
+ const val = event.target.value;
+ if (REGEX_PASSWORD.test(val)) {
+ setHasChanged(true);
+ } else {
+ setHasChanged(false);
+ }
+ };
+
// Default auto-generated key
const defaultKey = generateRndKey();
return (
-
+ }
+ rules={PASSWORD_COMPLEXITY_RULES}
+ >
+
- -
+
-
-
-