Skip to content

Commit f9963b4

Browse files
committed
Fixed a bug where it was possible to create multiple accounts with the same display name, added error messages for the registration page
1 parent e550f75 commit f9963b4

File tree

3 files changed

+177
-59
lines changed

3 files changed

+177
-59
lines changed

ATBPServer/post-requests.js

Lines changed: 59 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,41 +6,82 @@ const bcrypt = require('bcrypt');
66
const dbOp = require('./db-operations.js');
77
const crypto = require('crypto');
88

9+
function escapeRegex(string) {
10+
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
11+
}
12+
913
module.exports = {
10-
handleRegister: function (username, password, names, forgot, collection) {
14+
handleRegister: function (username, password, selectedNameParts, forgot, collection) {
1115
return new Promise(function (resolve, reject) {
1216
bcrypt.hash(password, 10, (err, hash) => {
13-
var name = '';
14-
for (var i in names) {
15-
name += names[i];
16-
if (i != names.length - 1 && names[i] != '') name += ' ';
17+
if (err) {
18+
console.error("Bcrypt password hashing error:", err);
19+
return reject(new Error("Password hashing failed"));
20+
}
21+
22+
let validNameParts = selectedNameParts.filter(n => n && n.trim() !== '');
23+
const finalDisplayNameForStorage = validNameParts.join(' ');
24+
25+
if (validNameParts.length < 2) {
26+
return reject(new Error("Insufficient display name parts selected."));
27+
}
28+
29+
let baseDisplayNameForLookup = finalDisplayNameForStorage;
30+
31+
const prefixRegex = /^(?:\[DEV\]\s*|\[ATBPDEV\]\s*)/i;
32+
baseDisplayNameForLookup = baseDisplayNameForLookup.replace(prefixRegex, '').trim();
33+
34+
if (!baseDisplayNameForLookup) {
35+
return reject(new Error("A valid core display name is required."));
1736
}
37+
38+
const escapedUsername = escapeRegex(username);
39+
const escapedBaseDisplayName = escapeRegex(baseDisplayNameForLookup);
40+
41+
const displayNameConflictRegex = new RegExp(
42+
`^(?:${prefixRegex.source})?${escapedBaseDisplayName}$`,
43+
'i'
44+
);
45+
1846
collection
1947
.findOne({
2048
$or: [
21-
{ 'user.TEGid': { $regex: new RegExp(`^${username}$`, 'i') } },
22-
{
23-
'user.dname': name
24-
.replace('[DEV] ', '')
25-
.replace('[ATBPDEV] ', ''),
26-
},
49+
{ 'user.TEGid': { $regex: new RegExp(`^${escapedUsername}$`, 'i') } },
50+
{ 'user.dname': displayNameConflictRegex },
2751
],
2852
})
29-
.then((user) => {
30-
if (user != null) {
31-
resolve('login');
53+
.then((existingUser) => {
54+
if (existingUser != null) {
55+
56+
if (existingUser.user.TEGid.toLowerCase() === username.toLowerCase()) {
57+
resolve('usernameTaken');
58+
} else {
59+
// If the username didn't match, the displayNameConflictRegex must have.
60+
resolve('displayNameTaken');
61+
}
3262
} else {
33-
bcrypt.hash(forgot, 10, (er, has) => {
63+
bcrypt.hash(forgot, 10, (er, forgotHash) => {
64+
if (er) {
65+
console.error("Bcrypt secret phrase hashing error:", er);
66+
return reject(new Error("Secret phrase hashing failed"));
67+
}
68+
3469
dbOp
35-
.createNewUser(username, name, hash, has, collection)
70+
.createNewUser(username, finalDisplayNameForStorage, hash, forgotHash, collection)
3671
.then((u) => {
3772
resolve(u);
3873
})
39-
.catch(console.error);
74+
.catch(dbErr => {
75+
console.error("Error in createNewUser:", dbErr);
76+
reject(dbErr);
77+
});
4078
});
4179
}
4280
})
43-
.catch(console.error);
81+
.catch(queryErr => {
82+
console.error("Error in findOne user check:", queryErr);
83+
reject(queryErr);
84+
});
4485
});
4586
});
4687
},

ATBPServer/server.js

Lines changed: 61 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -873,61 +873,85 @@ mongoClient.connect((err) => {
873873

874874
app.post('/auth/register', (req, res) => {
875875
var nameCount = 0;
876-
if (
877-
req.body.name1 != '' &&
878-
displayNames.list1.includes(getLowerCaseName(req.body.name1))
879-
)
876+
const name1 = req.body.name1 ? req.body.name1.trim() : '';
877+
const name2 = req.body.name2 ? req.body.name2.trim() : '';
878+
const name3 = req.body.name3 ? req.body.name3.trim() : '';
879+
880+
if (name1 !== '' && displayNames.list1.includes(getLowerCaseName(name1))) {
880881
nameCount++;
881-
else if (req.body.name1 != '') nameCount = -100;
882-
if (
883-
req.body.name2 != '' &&
884-
displayNames.list2.includes(getLowerCaseName(req.body.name2))
885-
)
882+
} else if (name1 !== '') {
883+
nameCount = -100;
884+
}
885+
886+
if (name2 !== '' && displayNames.list2.includes(getLowerCaseName(name2))) {
886887
nameCount++;
887-
else if (req.body.name2 != '') nameCount = -100;
888-
if (
889-
req.body.name3 != '' &&
890-
displayNames.list3.includes(getLowerCaseName(req.body.name3))
891-
)
888+
} else if (name2 !== '') {
889+
nameCount = -100;
890+
}
891+
892+
if (name3 !== '' && displayNames.list3.includes(getLowerCaseName(name3))) {
892893
nameCount++;
893-
else if (req.body.name3 != '') nameCount = -100;
894+
} else if (name3 !== '') {
895+
nameCount = -100;
896+
}
897+
898+
const username = req.body.username ? req.body.username.trim() : '';
899+
const password = req.body.password;
900+
const confirmPassword = req.body.confirm;
901+
const forgotPhrase = req.body.forgot ? req.body.forgot.trim() : '';
894902

895903
if (
896-
req.body.username != '' &&
897-
req.body.password != '' &&
898-
nameCount > 1 &&
899-
req.body.password == req.body.confirm
904+
username !== '' &&
905+
password !== '' &&
906+
forgotPhrase !== '' &&
907+
nameCount >= 2 && // Require at least 2 valid parts for the display name
908+
password === confirmPassword
900909
) {
901-
var names = [req.body.name1, req.body.name2, req.body.name3];
910+
var namesForDisplay = [name1, name2, name3].filter(n => n !== ''); // Filter out empty parts for display name construction
911+
902912
postRequest
903913
.handleRegister(
904-
req.body.username,
905-
req.body.password,
906-
names,
907-
req.body.forgot,
914+
username,
915+
password,
916+
namesForDisplay,
917+
forgotPhrase,
908918
playerCollection
909919
)
910-
.then((user) => {
911-
if (user == 'login') {
912-
res.redirect('/login?exists=true');
913-
} else {
914-
res.cookie('TEGid', user.user.TEGid);
915-
res.cookie('authid', user.user.authid);
916-
res.cookie('dname', user.user.dname);
917-
res.cookie('authpass', user.user.authpass);
918-
var date = Date.parse(user.session.expires_at);
919-
res.cookie('session_token', user.session.token, {
920+
.then((result) => {
921+
if (result === 'usernameTaken') {
922+
res.redirect('/register?usernameTaken=true');
923+
} else if (result === 'displayNameTaken') {
924+
res.redirect('/register?displayNameTaken=true');
925+
} else if (typeof result === 'object' && result.user && result.session) { // Successful registration
926+
res.cookie('TEGid', result.user.TEGid);
927+
res.cookie('authid', result.user.authid);
928+
res.cookie('dname', result.user.dname);
929+
res.cookie('authpass', result.user.authpass);
930+
var date = Date.parse(result.session.expires_at);
931+
res.cookie('session_token', result.session.token, {
920932
maxAge: date.valueOf() - Date.now(),
921933
});
922934
res.cookie('logged', true);
923935
res.redirect(config.httpserver.url);
936+
} else {
937+
console.warn("handleRegister returned unexpected result:", result);
938+
res.redirect('/register?failed=true&reason=unexpected');
924939
}
925940
})
926941
.catch((e) => {
927-
console.log(e);
928-
res.redirect('/register?failed=true');
942+
console.error("Error during registration process:", e);
943+
944+
res.redirect('/register?failed=true&reason=servererror');
929945
});
930-
} else res.redirect('/register?failed=true');
946+
} else {
947+
// Initial validation failed (empty fields, passwords don't match, invalid display name count)
948+
let reason = 'validation';
949+
if (password !== confirmPassword) reason = 'passwordmismatch';
950+
if (nameCount < 2 && nameCount > -100) reason = 'displaynameparts'; // if nameCount is -100, it's an invalid selection
951+
else if (nameCount === -100) reason = 'invaliddisplayname';
952+
953+
res.redirect(`/register?failed=true&reason=${reason}`);
954+
}
931955
});
932956

933957
app.post('/auth/forgot', (req, res) => {

ATBPServer/views/register.ejs

Lines changed: 57 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -197,16 +197,69 @@
197197
<script defer type="text/javascript" src="js/bootstrap.bundle.min.js"></script>
198198

199199
<script type="text/javascript">
200-
const failed = window.location.search.includes('failed');
201-
const exists = window.location.search.includes('exists');
202-
if (failed) window.alert("Registration failed!");
203-
else if (exists) window.alert("User already exists! Please login.");
200+
const params = new URLSearchParams(window.location.search);
201+
const failed = params.get('failed') === 'true';
202+
const usernameTaken = params.get('usernameTaken') === 'true';
203+
const displayNameTaken = params.get('displayNameTaken') === 'true';
204+
const reason = params.get('reason');
205+
206+
if (usernameTaken) {
207+
window.alert("Registration failed: Username is already taken. Please choose another.");
208+
} else if (displayNameTaken) {
209+
window.alert("Registration failed: Display Name is already taken. Please choose another.");
210+
} else if (failed) {
211+
let message = "Registration failed! Please check your inputs.";
212+
if (reason === 'passwordmismatch') {
213+
message = "Registration failed: Passwords do not match.";
214+
} else if (reason === 'displaynameparts') {
215+
message = "Registration failed: Please select at least two parts for your display name.";
216+
} else if (reason === 'invaliddisplayname') {
217+
message = "Registration failed: An invalid display name part was selected.";
218+
} else if (reason === 'servererror') {
219+
message = "Registration failed: A server error occurred. Please try again later.";
220+
} else if (reason === 'unexpected') {
221+
message = "Registration failed: An unexpected error occurred. Please try again.";
222+
}
223+
window.alert(message);
224+
}
204225
205226
function blockSpecialChar(e) {
206227
var format = /[ `!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?~]/;
207228
return e.key != undefined ? !format.test(e.key) : !format.test(String.fromCharCode(e.charCode));
208229
}
209230
231+
232+
document.addEventListener('DOMContentLoaded', function() {
233+
const form = document.getElementById('form');
234+
const passwordInput = document.getElementById('password');
235+
const confirmInput = document.getElementById('confirm');
236+
237+
if (form && passwordInput && confirmInput) {
238+
form.addEventListener('submit', function(event) {
239+
if (passwordInput.value !== confirmInput.value) {
240+
alert("Passwords do not match!");
241+
event.preventDefault(); // Prevent form submission
242+
confirmInput.focus();
243+
}
244+
245+
const name1 = document.getElementById('name1').value;
246+
const name2 = document.getElementById('name2').value;
247+
const name3 = document.getElementById('name3').value;
248+
let selectedNameParts = 0;
249+
if (name1) selectedNameParts++;
250+
if (name2) selectedNameParts++;
251+
if (name3) selectedNameParts++;
252+
253+
if (selectedNameParts < 2) {
254+
alert("Please select at least two parts for your display name.");
255+
event.preventDefault();
256+
257+
document.getElementById('name1').focus();
258+
}
259+
});
260+
}
261+
});
262+
210263
</script>
211264
<div class="container" style="width: 32rem;">
212265
<div class="card text-center">

0 commit comments

Comments
 (0)