Skip to content

Commit 0f9b70d

Browse files
authored
Merge pull request #67 from rvykydal/add-full-name-validation
Users: full name validation
2 parents 3d3c095 + 14918c6 commit 0f9b70d

File tree

4 files changed

+76
-45
lines changed

4 files changed

+76
-45
lines changed

src/components/Password.jsx

+20-22
Original file line numberDiff line numberDiff line change
@@ -120,38 +120,36 @@ const passwordStrengthLabel = (idPrefix, strength, strengthLevels) => {
120120
export const PasswordFormFields = ({
121121
idPrefix,
122122
policy,
123-
initialPassword,
123+
password,
124+
setPassword,
124125
passwordLabel,
125-
onChange,
126-
initialConfirmPassword,
126+
confirmPassword,
127+
setConfirmPassword,
127128
confirmPasswordLabel,
128-
onConfirmChange,
129129
rules,
130130
setIsValid,
131131
}) => {
132132
const [passwordHidden, setPasswordHidden] = useState(true);
133133
const [confirmHidden, setConfirmHidden] = useState(true);
134-
const [_password, _setPassword] = useState(initialPassword);
135-
const [_confirmPassword, _setConfirmPassword] = useState(initialConfirmPassword);
136-
const [password, setPassword] = useState(initialPassword);
137-
const [confirmPassword, setConfirmPassword] = useState(initialConfirmPassword);
134+
const [checkPassword, setCheckPassword] = useState(password);
135+
const [checkConfirmPassword, setCheckConfirmPassword] = useState(confirmPassword);
138136
const [passwordStrength, setPasswordStrength] = useState("");
139137

140138
useEffect(() => {
141-
debounce(300, () => { setPassword(_password); onChange(_password) })();
142-
}, [_password, onChange]);
139+
debounce(300, () => setCheckPassword(password))();
140+
}, [password, setCheckPassword]);
143141

144142
useEffect(() => {
145-
debounce(300, () => { setConfirmPassword(_confirmPassword); onConfirmChange(_confirmPassword) })();
146-
}, [_confirmPassword, onConfirmChange]);
143+
debounce(300, () => setCheckConfirmPassword(confirmPassword))();
144+
}, [confirmPassword, setCheckConfirmPassword]);
147145

148146
const ruleResults = useMemo(() => {
149-
return getRuleResults(rules, policy, password);
150-
}, [policy, password, rules]);
147+
return getRuleResults(rules, policy, checkPassword);
148+
}, [policy, checkPassword, rules]);
151149

152150
const ruleConfirmMatches = useMemo(() => {
153-
return password.length > 0 ? password === confirmPassword : null;
154-
}, [password, confirmPassword]);
151+
return checkPassword.length > 0 ? checkPassword === checkConfirmPassword : null;
152+
}, [checkPassword, checkConfirmPassword]);
155153

156154
const ruleHelperItems = ruleResults.map(rule => {
157155
let variant = rule.isSatisfied === null ? "indeterminate" : rule.isSatisfied ? "success" : "error";
@@ -182,11 +180,11 @@ export const PasswordFormFields = ({
182180

183181
useEffect(() => {
184182
const updatePasswordStrength = async () => {
185-
const _passwordStrength = await getPasswordStrength(password, strengthLevels);
183+
const _passwordStrength = await getPasswordStrength(checkPassword, strengthLevels);
186184
setPasswordStrength(_passwordStrength);
187185
};
188186
updatePasswordStrength();
189-
}, [password, strengthLevels]);
187+
}, [checkPassword, strengthLevels]);
190188

191189
useEffect(() => {
192190
setIsValid(
@@ -206,8 +204,8 @@ export const PasswordFormFields = ({
206204
<InputGroupItem isFill>
207205
<TextInput
208206
type={passwordHidden ? "password" : "text"}
209-
value={_password}
210-
onChange={(_event, val) => _setPassword(val)}
207+
value={password}
208+
onChange={(_event, val) => setPassword(val)}
211209
id={idPrefix + "-password-field"}
212210
/>
213211
</InputGroupItem>
@@ -233,8 +231,8 @@ export const PasswordFormFields = ({
233231
<InputGroup>
234232
<InputGroupItem isFill><TextInput
235233
type={confirmHidden ? "password" : "text"}
236-
value={_confirmPassword}
237-
onChange={(_event, val) => _setConfirmPassword(val)}
234+
value={confirmPassword}
235+
onChange={(_event, val) => setConfirmPassword(val)}
238236
id={idPrefix + "-password-confirm-field"}
239237
/>
240238
</InputGroupItem>

src/components/storage/DiskEncryption.jsx

+4-4
Original file line numberDiff line numberDiff line change
@@ -88,13 +88,13 @@ export const DiskEncryption = ({
8888
<PasswordFormFields
8989
idPrefix={idPrefix}
9090
policy={luksPolicy}
91-
initialPassword={password}
91+
password={password}
92+
setPassword={setPassword}
9293
passwordLabel={_("Passphrase")}
93-
initialConfirmPassword={confirmPassword}
94+
confirmPassword={confirmPassword}
95+
setConfirmPassword={setConfirmPassword}
9496
confirmPasswordLabel={_("Confirm passphrase")}
9597
rules={[ruleLength, ruleAscii]}
96-
onChange={setPassword}
97-
onConfirmChange={setConfirmPassword}
9898
setIsValid={setIsFormValid}
9999
/>
100100
);

src/components/users/Accounts.jsx

+47-17
Original file line numberDiff line numberDiff line change
@@ -102,51 +102,70 @@ const CreateAccount = ({
102102
setAccounts,
103103
}) => {
104104
const [fullName, setFullName] = useState(accounts.fullName);
105-
const [_userName, _setUserName] = useState(accounts.userName);
105+
const [checkFullName, setCheckFullName] = useState(accounts.fullName);
106+
const [fullNameInvalidHint, setFullNameInvalidHint] = useState("");
107+
const [isFullNameValid, setIsFullNameValid] = useState(null);
106108
const [userName, setUserName] = useState(accounts.userName);
109+
const [checkUserName, setCheckUserName] = useState(accounts.userName);
107110
const [userNameInvalidHint, setUserNameInvalidHint] = useState("");
108111
const [isUserNameValid, setIsUserNameValid] = useState(null);
109112
const [password, setPassword] = useState(accounts.password);
110113
const [confirmPassword, setConfirmPassword] = useState(accounts.confirmPassword);
111114
const [isPasswordValid, setIsPasswordValid] = useState(false);
112115

113116
useEffect(() => {
114-
debounce(300, () => setUserName(_userName))();
115-
}, [_userName, setUserName]);
117+
debounce(300, () => setCheckUserName(userName))();
118+
}, [userName, setCheckUserName]);
116119

117120
useEffect(() => {
118-
setIsUserValid(isPasswordValid && isUserNameValid);
119-
}, [setIsUserValid, isPasswordValid, isUserNameValid]);
121+
debounce(300, () => setCheckFullName(fullName))();
122+
}, [fullName, setCheckFullName]);
123+
124+
useEffect(() => {
125+
setIsUserValid(isPasswordValid && isUserNameValid && isFullNameValid !== false);
126+
}, [setIsUserValid, isPasswordValid, isUserNameValid, isFullNameValid]);
120127

121128
useEffect(() => {
122129
let valid = true;
123130
setUserNameInvalidHint("");
124-
if (userName.length === 0) {
131+
if (checkUserName.length === 0) {
125132
valid = null;
126-
} else if (userName.length > 32) {
133+
} else if (checkUserName.length > 32) {
127134
valid = false;
128135
setUserNameInvalidHint(_("User names must be shorter than 33 characters"));
129-
} else if (reservedNames.includes(userName)) {
136+
} else if (reservedNames.includes(checkUserName)) {
130137
valid = false;
131138
setUserNameInvalidHint(_("User name must not be a reserved word"));
132-
} else if (isUserNameWithInvalidCharacters(userName)) {
139+
} else if (isUserNameWithInvalidCharacters(checkUserName)) {
133140
valid = false;
134141
setUserNameInvalidHint(cockpit.format(_("User name may only contain: letters from a-z, digits 0-9, dash $0, period $1, underscore $2"), "-", ".", "_"));
135142
}
136143
setIsUserNameValid(valid);
137-
}, [userName]);
144+
}, [checkUserName]);
145+
146+
useEffect(() => {
147+
let valid = true;
148+
setFullNameInvalidHint("");
149+
if (checkFullName.length === 0) {
150+
valid = null;
151+
} else if (!checkFullName.match(/^[^:]*$/)) {
152+
valid = false;
153+
setFullNameInvalidHint(_("Full name cannot contain colon characters"));
154+
}
155+
setIsFullNameValid(valid);
156+
}, [checkFullName]);
138157

139158
const passphraseForm = (
140159
<PasswordFormFields
141160
idPrefix={idPrefix}
142161
policy={passwordPolicy}
143-
initialPassword={password}
162+
password={password}
163+
setPassword={setPassword}
144164
passwordLabel={_("Passphrase")}
145-
initialConfirmPassword={confirmPassword}
165+
confirmPassword={confirmPassword}
166+
setConfirmPassword={setConfirmPassword}
146167
confirmPasswordLabel={_("Confirm passphrase")}
147168
rules={[ruleLength]}
148-
onChange={setPassword}
149-
onConfirmChange={setConfirmPassword}
150169
setIsValid={setIsPasswordValid}
151170
/>
152171
);
@@ -155,7 +174,9 @@ const CreateAccount = ({
155174
setAccounts(ac => ({ ...ac, fullName, userName, password, confirmPassword }));
156175
}, [setAccounts, fullName, userName, password, confirmPassword]);
157176

158-
const userNameValidated = isUserNameValid === null ? "default" : isUserNameValid ? "success" : "error";
177+
const getValidatedVariant = (valid) => valid === null ? "default" : valid ? "success" : "error";
178+
const userNameValidated = getValidatedVariant(isUserNameValid);
179+
const fullNameValidated = getValidatedVariant(isFullNameValid);
159180

160181
return (
161182
<Form
@@ -177,7 +198,16 @@ const CreateAccount = ({
177198
id={idPrefix + "-full-name"}
178199
value={fullName}
179200
onChange={(_event, val) => setFullName(val)}
201+
validated={fullNameValidated}
180202
/>
203+
{fullNameValidated === "error" &&
204+
<FormHelperText>
205+
<HelperText>
206+
<HelperTextItem variant={fullNameValidated}>
207+
{fullNameInvalidHint}
208+
</HelperTextItem>
209+
</HelperText>
210+
</FormHelperText>}
181211
</FormGroup>
182212
<FormGroup
183213
label={_("User name")}
@@ -186,8 +216,8 @@ const CreateAccount = ({
186216
<InputGroup id={idPrefix + "-user-name-input-group"}>
187217
<TextInput
188218
id={idPrefix + "-user-name"}
189-
value={_userName}
190-
onChange={(_event, val) => _setUserName(val)}
219+
value={userName}
220+
onChange={(_event, val) => setUserName(val)}
191221
validated={userNameValidated}
192222
/>
193223
</InputGroup>

test/check-users

+5-2
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ from installer import Installer
2121
from password import Password
2222
from review import Review
2323
from users import Users, CREATE_ACCOUNT_ID_PREFIX, create_user, dbus_reset_users
24-
from testlib import nondestructive, test_main, sit # pylint: disable=import-error
24+
from testlib import nondestructive, test_main # pylint: disable=import-error
2525

2626
@nondestructive
2727
class TestUsers(anacondalib.VirtInstallMachineCase):
@@ -113,9 +113,12 @@ class TestUsers(anacondalib.VirtInstallMachineCase):
113113
# Test full name validation
114114
u.set_full_name("")
115115
i.check_next_disabled(disabled=False)
116+
u.set_full_name("colon:isforbidden")
117+
i.check_next_disabled()
116118
u.set_full_name(full_name)
119+
i.check_next_disabled(disabled=False)
117120

118-
# Test account validation
121+
# Test user name validation
119122
# FIXME this should be tested by unit tests?
120123
invalid_user_names = [
121124
"",

0 commit comments

Comments
 (0)