Merge pull request #424 from mz8i/feature/286-validate-username

Feature 286: validate username
This commit is contained in:
mz8i 2019-09-30 12:43:36 +01:00 committed by GitHub
commit 0a5c14762e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 45 additions and 5 deletions

View File

@ -5,6 +5,7 @@ import express from 'express';
import * as userService from '../services/user';
import * as passwordResetService from '../services/passwordReset';
import { TokenVerificationError } from '../services/passwordReset';
import { ValidationError } from '../validation';
function createUser(req, res) {
const user = req.body;
@ -86,6 +87,8 @@ async function resetPassword(req: express.Request, res: express.Response) {
} catch (err) {
if (err instanceof TokenVerificationError) {
return res.send({ error: 'Could not verify token' });
} else if (err instanceof ValidationError) {
return res.send({ error: err.message});
}
throw err;

View File

@ -5,6 +5,7 @@ import nodemailer from 'nodemailer';
import db from '../../db';
import * as userService from './user';
import { transporter } from './email';
import { validatePassword } from '../validation';
/**
@ -24,6 +25,7 @@ export async function sendPasswordResetToken(email: string, siteOrigin: string):
}
export async function resetPassword(passwordResetToken: string, newPassword: string): Promise<void> {
validatePassword(newPassword);
const userId = await verifyPasswordResetToken(passwordResetToken);
if (userId != undefined) {
await updatePasswordForUser(userId, newPassword);

View File

@ -5,14 +5,18 @@
import { errors } from 'pg-promise';
import db from '../../db';
import { validateUsername, ValidationError, validatePassword } from '../validation';
function createUser(user) {
if (!user.password || user.password.length < 8) {
return Promise.reject({ error: 'Password must be at least 8 characters' })
}
if (user.password.length > 70) {
return Promise.reject({ error: 'Password must be at most 70 characters' })
try {
validateUsername(user.username);
validatePassword(user.password);
} catch(err) {
if (err instanceof ValidationError) {
return Promise.reject({ error: err.message });
} else throw err;
}
return db.one(
`INSERT
INTO users (

25
app/src/api/validation.ts Normal file
View File

@ -0,0 +1,25 @@
class ValidationError extends Error {
constructor(message) {
super(message);
this.name = 'ValidationError';
}
}
function validateUsername(username: string): void {
if (username == undefined) throw new ValidationError('Username is required');
if (!username.match(/^\w+$/)) throw new ValidationError('Username can only contain alphanumeric characters and underscore');
if (username.length < 4) throw new ValidationError('Username must be at least 4 characters long');
if (username.length > 30) throw new ValidationError('Username must be at most 30 characters long');
}
function validatePassword(password: string): void {
if (password == undefined) throw new ValidationError('Password is required');
if (password.length < 8) throw new ValidationError('Password must be at least 8 characters long');
if (password.length > 128) throw new ValidationError('Password must be at most 128 characters long');
}
export {
ValidationError,
validateUsername,
validatePassword
};

View File

@ -94,6 +94,10 @@ class SignUp extends Component<any, any> { // TODO: add proper types
className="form-control" type="text"
value={this.state.username} onChange={this.handleChange}
placeholder="not-your-real-name" required
minLength={4}
maxLength={30}
pattern="\w+"
title="Usernames can contain only letters, numbers and the underscore"
/>
<label htmlFor="email">Email (optional)</label>
@ -115,6 +119,8 @@ class SignUp extends Component<any, any> { // TODO: add proper types
type={(this.state.show_password)? 'text': 'password'}
value={this.state.password} onChange={this.handleChange}
required
minLength={8}
maxLength={128}
/>
<div className="form-check">