diff --git a/src/backend/src/api/APIError.js b/src/backend/src/api/APIError.js index 5a6b1321..55ab0bd6 100644 --- a/src/backend/src/api/APIError.js +++ b/src/backend/src/api/APIError.js @@ -306,6 +306,10 @@ module.exports = class APIError { status: 409, message: ({ email }) => `Email ${quot(email)} is already in use.`, }, + 'email_not_allowed': { + status: 400, + message: ({ email }) => `The email ${quot(email)} is not allowed.`, + }, 'username_already_in_use': { status: 409, diff --git a/src/backend/src/routers/save_account.js b/src/backend/src/routers/save_account.js index 929b0e10..317e68a6 100644 --- a/src/backend/src/routers/save_account.js +++ b/src/backend/src/routers/save_account.js @@ -73,13 +73,9 @@ router.post('/save_account', auth, express.json(), async (req, res, next)=>{ const svc_cleanEmail = req.services.get('clean-email') const clean_email = svc_cleanEmail.clean(req.body.email); - - if ( can(config.blocked_email_domains, 'iterate') ) { - for ( const suffix of config.blocked_email_domains ) { - if ( clean_email.endsWith(suffix) ) { - return res.status(400).send('This email domain is not allowed.'); - } - } + + if ( ! svc_cleanEmail.validate(clean_email) ) { + return res.status(400).send('This email domain is not allowed.'); } const svc_edgeRateLimit = req.services.get('edge-rate-limit'); diff --git a/src/backend/src/routers/signup.js b/src/backend/src/routers/signup.js index 06f7905e..18f11a86 100644 --- a/src/backend/src/routers/signup.js +++ b/src/backend/src/routers/signup.js @@ -147,12 +147,8 @@ module.exports = eggspress(['/signup'], { const svc_cleanEmail = req.services.get('clean-email'); const clean_email = svc_cleanEmail.clean(req.body.email); - if ( can(config.blocked_email_domains, 'iterate') ) { - for ( const suffix of config.blocked_email_domains ) { - if ( clean_email.endsWith(suffix) ) { - return res.status(400).send('This email domain is not allowed.'); - } - } + if ( ! svc_cleanEmail.validate(clean_email) ) { + return res.status(400).send('This email domain is not allowed'); } // duplicate username check diff --git a/src/backend/src/routers/user-protected/change-email.js b/src/backend/src/routers/user-protected/change-email.js index 01a442bc..d96a0d72 100644 --- a/src/backend/src/routers/user-protected/change-email.js +++ b/src/backend/src/routers/user-protected/change-email.js @@ -49,12 +49,20 @@ module.exports = { const svc_cleanEmail = req.services.get('clean-email'); const clean_email = svc_cleanEmail.clean(new_email); + if ( ! svc_cleanEmail.validate(clean_email) ) { + throw APIError.create('email_not_allowed', undefined, { + email: clean_email, + }); + } + // check if email is already in use const db = req.services.get('database').get(DB_WRITE, 'auth'); const rows = await db.read( 'SELECT COUNT(*) AS `count` FROM `user` WHERE (`email` = ? OR `clean_email` = ?) AND `email_confirmed` = 1', [new_email, clean_email] ); + + // TODO: DRY: signup.js, save_account.js if ( rows[0].count > 0 ) { throw APIError.create('email_already_in_use', null, { email: new_email }); } @@ -84,6 +92,18 @@ module.exports = { [new_email, token, user.id] ); + // Update email change audit table + await db.write( + 'INSERT INTO `user_update_audit` ' + + '(`user_id`, `user_id_keep`, `old_email`, `new_email`, `reason`) ' + + 'VALUES (?, ?, ?, ?, ?)', + [ + req.user.id, req.user.id, + old_email, new_email, + 'change_username' + ] + ); + res.send({ success: true }); } }; diff --git a/src/backend/src/services/CleanEmailService.js b/src/backend/src/services/CleanEmailService.js index 91614996..70b22566 100644 --- a/src/backend/src/services/CleanEmailService.js +++ b/src/backend/src/services/CleanEmailService.js @@ -1,3 +1,4 @@ +const { can } = require("../util/langutil"); const BaseService = require("./BaseService"); class CleanEmailService extends BaseService { @@ -99,6 +100,21 @@ class CleanEmailService extends BaseService { return eml.local + '@' + eml.domain; } + + validate (email) { + email = this.clean(email); + const config = this.global_config; + + if ( can(config.blocked_email_domains, 'iterate') ) { + for ( const suffix of config.blocked_email_domains ) { + if ( email.endsWith(suffix) ) { + return false; + } + } + } + + return true; + } _test ({ assert }) { const cases = [