mirror of
https://github.com/HeyPuter/puter.git
synced 2026-01-07 05:30:31 -06:00
fix: bad amount of sql params (#1880)
This commit is contained in:
@@ -16,22 +16,22 @@
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
"use strict"
|
||||
const {get_taskbar_items, send_email_verification_code, send_email_verification_token, username_exists, invalidate_cached_user_by_id, get_user } = require('../helpers');
|
||||
'use strict';
|
||||
const { get_taskbar_items, send_email_verification_code, send_email_verification_token, username_exists, invalidate_cached_user_by_id, get_user } = require('../helpers');
|
||||
const config = require('../config');
|
||||
const eggspress = require('../api/eggspress');
|
||||
const { Context } = require('../util/context');
|
||||
const { DB_WRITE } = require('../services/database/consts');
|
||||
const { generate_identifier } = require('../util/identifier');
|
||||
const { is_temp_users_disabled: lazy_temp_users,
|
||||
is_user_signup_disabled: lazy_user_signup } = require("../helpers")
|
||||
const { is_temp_users_disabled: lazy_temp_users,
|
||||
is_user_signup_disabled: lazy_user_signup } = require('../helpers');
|
||||
const { requireCaptcha } = require('../modules/captcha/middleware/captcha-middleware');
|
||||
|
||||
async function generate_random_username () {
|
||||
async function generate_random_username() {
|
||||
let username;
|
||||
do {
|
||||
username = generate_identifier();
|
||||
} while (await username_exists(username));
|
||||
} while ( await username_exists(username) );
|
||||
return username;
|
||||
}
|
||||
|
||||
@@ -46,14 +46,16 @@ module.exports = eggspress(['/signup'], {
|
||||
no_bots: true,
|
||||
// puter_origin: false,
|
||||
shadow_ban_responder: (req, res) => {
|
||||
res.status(400).send(`email username mismatch; please provide a password`);
|
||||
}
|
||||
res.status(400).send('email username mismatch; please provide a password');
|
||||
},
|
||||
},
|
||||
mw: [requireCaptcha({ strictMode: true, eventType: 'signup' })], // Conditionally require captcha for signup
|
||||
}, async (req, res, next) => {
|
||||
// either api. subdomain or no subdomain
|
||||
if(require('../helpers').subdomain(req) !== 'api' && require('../helpers').subdomain(req) !== '')
|
||||
if ( require('../helpers').subdomain(req) !== 'api' && require('../helpers').subdomain(req) !== '' )
|
||||
{
|
||||
next();
|
||||
}
|
||||
|
||||
const svc_edgeRateLimit = req.services.get('edge-rate-limit');
|
||||
if ( ! svc_edgeRateLimit.check('signup') ) {
|
||||
@@ -62,31 +64,32 @@ module.exports = eggspress(['/signup'], {
|
||||
|
||||
// modules
|
||||
const db = req.services.get('database').get(DB_WRITE, 'auth');
|
||||
const bcrypt = require('bcrypt')
|
||||
const bcrypt = require('bcrypt');
|
||||
const { v4: uuidv4 } = require('uuid');
|
||||
const jwt = require('jsonwebtoken')
|
||||
const validator = require('validator')
|
||||
const jwt = require('jsonwebtoken');
|
||||
const validator = require('validator');
|
||||
let uuid_user;
|
||||
|
||||
const svc_auth = Context.get('services').get('auth');
|
||||
const svc_authAudit = Context.get('services').get('auth-audit');
|
||||
svc_authAudit.record({
|
||||
requester: Context.get('requester'),
|
||||
action: req.body.is_temp ? `signup:temp` : `signup:real`,
|
||||
action: req.body.is_temp ? 'signup:temp' : 'signup:real',
|
||||
body: req.body,
|
||||
});
|
||||
|
||||
// check bot trap, if `p102xyzname` is anything but an empty string it means
|
||||
// that a bot has filled the form
|
||||
// doesn't apply to temp users
|
||||
if(!req.body.is_temp && req.body.p102xyzname !== '')
|
||||
if ( !req.body.is_temp && req.body.p102xyzname !== '' )
|
||||
{
|
||||
return res.send();
|
||||
|
||||
}
|
||||
|
||||
// cloudflare turnstile validation
|
||||
//
|
||||
// ref: https://developers.cloudflare.com/turnstile/get-started/server-side-validation/
|
||||
if (config.services?.['cloudflare-turnstile']?.enabled) {
|
||||
if ( config.services?.['cloudflare-turnstile']?.enabled ) {
|
||||
const formData = new FormData();
|
||||
formData.append('secret', config.services?.['cloudflare-turnstile']?.secret_key);
|
||||
formData.append('response', req.body['cf-turnstile-response']);
|
||||
@@ -94,12 +97,14 @@ module.exports = eggspress(['/signup'], {
|
||||
|
||||
const response = await fetch('https://challenges.cloudflare.com/turnstile/v0/siteverify', {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
body: formData,
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
if (!result.success)
|
||||
if ( !result.success )
|
||||
{
|
||||
return res.status(400).send('captcha verification failed');
|
||||
}
|
||||
}
|
||||
|
||||
// send event
|
||||
@@ -112,7 +117,7 @@ module.exports = eggspress(['/signup'], {
|
||||
};
|
||||
|
||||
const svc_event = Context.get('services').get('event');
|
||||
await svc_event.emit('puter.signup', event)
|
||||
await svc_event.emit('puter.signup', event);
|
||||
|
||||
if ( ! event.allow ) {
|
||||
return res.status(400).send(event.error ?? 'You are not allowed to sign up.');
|
||||
@@ -120,9 +125,7 @@ module.exports = eggspress(['/signup'], {
|
||||
|
||||
// check if user is already logged in
|
||||
if ( req.body.is_temp && req.cookies[config.cookie_name] ) {
|
||||
const { user, token } = await svc_auth.check_session(
|
||||
req.cookies[config.cookie_name]
|
||||
);
|
||||
const { user, token } = await svc_auth.check_session(req.cookies[config.cookie_name]);
|
||||
res.cookie(config.cookie_name, token, {
|
||||
sameSite: 'none',
|
||||
secure: true,
|
||||
@@ -141,7 +144,7 @@ module.exports = eggspress(['/signup'], {
|
||||
requires_email_confirmation: user.requires_email_confirmation,
|
||||
is_temp: (user.password === null && user.email === null),
|
||||
taskbar_items: await get_taskbar_items(user),
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -149,19 +152,19 @@ module.exports = eggspress(['/signup'], {
|
||||
const is_temp_users_disabled = await lazy_temp_users();
|
||||
const is_user_signup_disabled = await lazy_user_signup();
|
||||
|
||||
if (is_temp_users_disabled && is_user_signup_disabled) {
|
||||
if ( is_temp_users_disabled && is_user_signup_disabled ) {
|
||||
return res.status(403).send('User signup and Temporary users are disabled.');
|
||||
}
|
||||
|
||||
if (!req.body.is_temp && is_user_signup_disabled) {
|
||||
if ( !req.body.is_temp && is_user_signup_disabled ) {
|
||||
return res.status(403).send('User signup is disabled.');
|
||||
}
|
||||
}
|
||||
|
||||
if (req.body.is_temp && is_temp_users_disabled) {
|
||||
if ( req.body.is_temp && is_temp_users_disabled ) {
|
||||
return res.status(403).send('Temporary users are disabled.');
|
||||
}
|
||||
|
||||
if (req.body.is_temp && event.no_temp_user) {
|
||||
if ( req.body.is_temp && event.no_temp_user ) {
|
||||
return res.status(403).send('You must login or signup.');
|
||||
}
|
||||
|
||||
@@ -169,74 +172,101 @@ module.exports = eggspress(['/signup'], {
|
||||
req.body.username = req.body.username ?? await generate_random_username();
|
||||
req.body.email = req.body.email ?? req.body.username + '@gmail.com';
|
||||
req.body.password = req.body.password ?? 'sadasdfasdfsadfsa';
|
||||
|
||||
|
||||
// send_confirmation_code
|
||||
req.body.send_confirmation_code = req.body.send_confirmation_code ?? true;
|
||||
|
||||
// username is required
|
||||
if(!req.body.username)
|
||||
return res.status(400).send('Username is required')
|
||||
if ( !req.body.username )
|
||||
{
|
||||
return res.status(400).send('Username is required');
|
||||
}
|
||||
// username must be a string
|
||||
else if (typeof req.body.username !== 'string')
|
||||
return res.status(400).send('username must be a string.')
|
||||
else if ( typeof req.body.username !== 'string' )
|
||||
{
|
||||
return res.status(400).send('username must be a string.');
|
||||
}
|
||||
// check if username is valid
|
||||
else if(!req.body.username.match(config.username_regex))
|
||||
return res.status(400).send('Username can only contain letters, numbers and underscore (_).')
|
||||
else if ( !req.body.username.match(config.username_regex) )
|
||||
{
|
||||
return res.status(400).send('Username can only contain letters, numbers and underscore (_).');
|
||||
}
|
||||
// check if username is of proper length
|
||||
else if(req.body.username.length > config.username_max_length)
|
||||
return res.status(400).send(`Username cannot be longer than ${config.username_max_length} characters.`)
|
||||
else if ( req.body.username.length > config.username_max_length )
|
||||
{
|
||||
return res.status(400).send(`Username cannot be longer than ${config.username_max_length} characters.`);
|
||||
}
|
||||
// check if username matches any reserved words
|
||||
else if(config.reserved_words.includes(req.body.username))
|
||||
return res.status(400).send({message: 'This username is not available.'});
|
||||
else if ( config.reserved_words.includes(req.body.username) )
|
||||
{
|
||||
return res.status(400).send({ message: 'This username is not available.' });
|
||||
}
|
||||
// TODO: DRY: change_email.js
|
||||
else if(!req.body.is_temp && !req.body.email)
|
||||
else if ( !req.body.is_temp && !req.body.email )
|
||||
{
|
||||
return res.status(400).send('Email is required');
|
||||
}
|
||||
// email, if present, must be a string
|
||||
else if (req.body.email && typeof req.body.email !== 'string')
|
||||
return res.status(400).send('email must be a string.')
|
||||
else if ( req.body.email && typeof req.body.email !== 'string' )
|
||||
{
|
||||
return res.status(400).send('email must be a string.');
|
||||
}
|
||||
// if email is present, validate it
|
||||
else if(!req.body.is_temp && !validator.isEmail(req.body.email))
|
||||
return res.status(400).send('Please enter a valid email address.')
|
||||
else if(!req.body.is_temp && !req.body.password)
|
||||
else if ( !req.body.is_temp && !validator.isEmail(req.body.email) )
|
||||
{
|
||||
return res.status(400).send('Please enter a valid email address.');
|
||||
}
|
||||
else if ( !req.body.is_temp && !req.body.password )
|
||||
{
|
||||
return res.status(400).send('Password is required');
|
||||
}
|
||||
// password, if present, must be a string
|
||||
else if (req.body.password && typeof req.body.password !== 'string')
|
||||
return res.status(400).send('password must be a string.')
|
||||
else if(!req.body.is_temp && req.body.password.length < config.min_pass_length)
|
||||
else if ( req.body.password && typeof req.body.password !== 'string' )
|
||||
{
|
||||
return res.status(400).send('password must be a string.');
|
||||
}
|
||||
else if ( !req.body.is_temp && req.body.password.length < config.min_pass_length )
|
||||
{
|
||||
return res.status(400).send(`Password must be at least ${config.min_pass_length} characters long.`);
|
||||
}
|
||||
|
||||
const svc_cleanEmail = req.services.get('clean-email');
|
||||
const clean_email = svc_cleanEmail.clean(req.body.email);
|
||||
|
||||
if (!req.body.is_temp && ! await svc_cleanEmail.validate(clean_email) ) {
|
||||
|
||||
if ( !req.body.is_temp && ! await svc_cleanEmail.validate(clean_email) ) {
|
||||
return res.status(400).send('This email does not seem to be valid.');
|
||||
}
|
||||
|
||||
// duplicate username check
|
||||
if(await username_exists(req.body.username))
|
||||
if ( await username_exists(req.body.username) )
|
||||
{
|
||||
return res.status(400).send('This username already exists in our database. Please use another one.');
|
||||
}
|
||||
// Email check is here :: Add condition for email_confirmed=1
|
||||
// duplicate email check (pseudo-users don't count)
|
||||
let rows2 = await db.read(
|
||||
`SELECT EXISTS(
|
||||
let rows2 = await db.read(`SELECT EXISTS(
|
||||
SELECT 1 FROM user WHERE (email=? OR clean_email=?) AND email_confirmed=1 AND password IS NOT NULL
|
||||
) AS email_exists`, [req.body.email, clean_email]);
|
||||
if(rows2[0].email_exists)
|
||||
if ( rows2[0].email_exists )
|
||||
{
|
||||
return res.status(400).send('This email already exists in our database. Please use another one.');
|
||||
}
|
||||
// get pseudo user, if exists
|
||||
let pseudo_user = await db.read(`SELECT * FROM user WHERE email = ? AND password IS NULL`, [req.body.email]);
|
||||
let pseudo_user = await db.read('SELECT * FROM user WHERE email = ? AND password IS NULL', [req.body.email]);
|
||||
pseudo_user = pseudo_user[0];
|
||||
// get uuid user, if exists
|
||||
if(req.body.uuid){
|
||||
uuid_user = await db.read(`SELECT * FROM user WHERE uuid = ? LIMIT 1`, [req.body.uuid]);
|
||||
if ( req.body.uuid ) {
|
||||
uuid_user = await db.read('SELECT * FROM user WHERE uuid = ? LIMIT 1', [req.body.uuid]);
|
||||
uuid_user = uuid_user[0];
|
||||
}
|
||||
|
||||
// email confirmation is required by default unless:
|
||||
// Pseudo user converting and matching uuid is provided
|
||||
let email_confirmation_required = 1;
|
||||
if(pseudo_user && uuid_user && pseudo_user.id === uuid_user.id)
|
||||
if ( pseudo_user && uuid_user && pseudo_user.id === uuid_user.id )
|
||||
{
|
||||
email_confirmation_required = 0;
|
||||
}
|
||||
|
||||
// -----------------------------------
|
||||
// Get referral user
|
||||
@@ -265,94 +295,120 @@ module.exports = eggspress(['/signup'], {
|
||||
server: config.server_id,
|
||||
};
|
||||
|
||||
if(pseudo_user === undefined){
|
||||
insert_res = await db.write(
|
||||
`INSERT INTO user
|
||||
if ( pseudo_user === undefined ) {
|
||||
insert_res = await db.write(`INSERT INTO user
|
||||
(
|
||||
username, email, clean_email, password, uuid, referrer, email_confirm_code, email_confirm_token, free_storage, referred_by, audit_metadata, signup_ip, signup_ip_forwarded, signup_user_agent, signup_origin, signup_server, metadata
|
||||
username,
|
||||
email,
|
||||
clean_email,
|
||||
password,
|
||||
uuid,
|
||||
referrer,
|
||||
email_confirm_code,
|
||||
email_confirm_token,
|
||||
free_storage,
|
||||
referred_by,
|
||||
audit_metadata,
|
||||
signup_ip,
|
||||
signup_ip_forwarded,
|
||||
signup_user_agent,
|
||||
signup_origin,
|
||||
signup_server,
|
||||
metadata
|
||||
)
|
||||
VALUES
|
||||
(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
[
|
||||
// username
|
||||
req.body.username,
|
||||
// email
|
||||
req.body.is_temp ? null : req.body.email,
|
||||
// normalized email
|
||||
req.body.is_temp ? null : clean_email,
|
||||
// password
|
||||
req.body.is_temp ? null : await bcrypt.hash(req.body.password, 8),
|
||||
// uuid
|
||||
user_uuid,
|
||||
// referrer
|
||||
req.body.referrer ?? null,
|
||||
// email_confirm_code
|
||||
'' + email_confirm_code,
|
||||
// email_confirm_token
|
||||
email_confirm_token,
|
||||
// free_storage
|
||||
config.storage_capacity,
|
||||
// referred_by
|
||||
referred_by_user ? referred_by_user.id : null,
|
||||
// audit_metadata
|
||||
JSON.stringify(audit_metadata),
|
||||
// signup_ip
|
||||
req.connection.remoteAddress ?? null,
|
||||
// signup_ip_fwd
|
||||
req.headers['x-forwarded-for'] ?? null,
|
||||
// signup_user_agent
|
||||
req.headers['user-agent'] ?? null,
|
||||
// signup_origin
|
||||
req.headers['origin'] ?? null,
|
||||
// signup_server
|
||||
config.server_id ?? null,
|
||||
// metadata
|
||||
{}
|
||||
]
|
||||
);
|
||||
(?,
|
||||
?,
|
||||
?,
|
||||
?,
|
||||
?,
|
||||
?,
|
||||
?,
|
||||
?,
|
||||
?,
|
||||
?,
|
||||
?,
|
||||
?,
|
||||
?,
|
||||
?,
|
||||
?,
|
||||
?,
|
||||
?)`,
|
||||
[
|
||||
// username
|
||||
req.body.username,
|
||||
// email
|
||||
req.body.is_temp ? null : req.body.email,
|
||||
// normalized email
|
||||
req.body.is_temp ? null : clean_email,
|
||||
// password
|
||||
req.body.is_temp ? null : await bcrypt.hash(req.body.password, 8),
|
||||
// uuid
|
||||
user_uuid,
|
||||
// referrer
|
||||
req.body.referrer ?? null,
|
||||
// email_confirm_code
|
||||
'' + email_confirm_code,
|
||||
// email_confirm_token
|
||||
email_confirm_token,
|
||||
// free_storage
|
||||
config.storage_capacity,
|
||||
// referred_by
|
||||
referred_by_user ? referred_by_user.id : null,
|
||||
// audit_metadata
|
||||
JSON.stringify(audit_metadata),
|
||||
// signup_ip
|
||||
req.connection.remoteAddress ?? null,
|
||||
// signup_ip_fwd
|
||||
req.headers['x-forwarded-for'] ?? null,
|
||||
// signup_user_agent
|
||||
req.headers['user-agent'] ?? null,
|
||||
// signup_origin
|
||||
req.headers['origin'] ?? null,
|
||||
// signup_server
|
||||
config.server_id ?? null,
|
||||
// metadata
|
||||
JSON.stringify({}),
|
||||
]);
|
||||
|
||||
// record activity
|
||||
db.write(
|
||||
'UPDATE `user` SET `last_activity_ts` = now() WHERE id=? LIMIT 1',
|
||||
[insert_res.insertId]
|
||||
);
|
||||
|
||||
db.write('UPDATE `user` SET `last_activity_ts` = now() WHERE id=? LIMIT 1',
|
||||
[insert_res.insertId]);
|
||||
|
||||
// TODO: cache group id
|
||||
const svc_group = req.services.get('group');
|
||||
await svc_group.add_users({
|
||||
uid: req.body.is_temp ?
|
||||
config.default_temp_group : config.default_user_group,
|
||||
users: [req.body.username]
|
||||
users: [req.body.username],
|
||||
});
|
||||
}
|
||||
// -----------------------------------
|
||||
// Pseudo User converting
|
||||
// -----------------------------------
|
||||
else{
|
||||
insert_res = await db.write(
|
||||
`UPDATE user SET
|
||||
else {
|
||||
insert_res = await db.write(`UPDATE user SET
|
||||
username = ?, password = ?, uuid = ?, email_confirm_code = ?, email_confirm_token = ?, email_confirmed = ?, requires_email_confirmation = 1,
|
||||
referred_by = ?
|
||||
WHERE id = ?`,
|
||||
[
|
||||
// username
|
||||
req.body.username,
|
||||
// password
|
||||
await bcrypt.hash(req.body.password, 8),
|
||||
// uuid
|
||||
user_uuid,
|
||||
// email_confirm_code
|
||||
'' + email_confirm_code,
|
||||
// email_confirm_token
|
||||
email_confirm_token,
|
||||
// email_confirmed
|
||||
!email_confirmation_required,
|
||||
// id
|
||||
pseudo_user.id,
|
||||
// referred_by
|
||||
referred_by_user ? referred_by_user.id : null,
|
||||
]
|
||||
);
|
||||
[
|
||||
// username
|
||||
req.body.username,
|
||||
// password
|
||||
await bcrypt.hash(req.body.password, 8),
|
||||
// uuid
|
||||
user_uuid,
|
||||
// email_confirm_code
|
||||
'' + email_confirm_code,
|
||||
// email_confirm_token
|
||||
email_confirm_token,
|
||||
// email_confirmed
|
||||
!email_confirmation_required,
|
||||
// id
|
||||
pseudo_user.id,
|
||||
// referred_by
|
||||
referred_by_user ? referred_by_user.id : null,
|
||||
]);
|
||||
|
||||
// TODO: cache group ids
|
||||
const svc_group = req.services.get('group');
|
||||
@@ -362,7 +418,7 @@ module.exports = eggspress(['/signup'], {
|
||||
});
|
||||
await svc_group.add_users({
|
||||
uid: config.default_user_group,
|
||||
users: [req.body.username]
|
||||
users: [req.body.username],
|
||||
});
|
||||
|
||||
// record activity
|
||||
@@ -374,10 +430,8 @@ module.exports = eggspress(['/signup'], {
|
||||
// todo if pseudo user, assign directly no need to do another DB lookup
|
||||
const user_id = (pseudo_user === undefined) ? insert_res.insertId : pseudo_user.id;
|
||||
|
||||
const [user] = await db.pread(
|
||||
'SELECT * FROM `user` WHERE `id` = ? LIMIT 1',
|
||||
[user_id]
|
||||
);
|
||||
const [user] = await db.pread('SELECT * FROM `user` WHERE `id` = ? LIMIT 1',
|
||||
[user_id]);
|
||||
|
||||
// create token for login
|
||||
const { token } = await svc_auth.create_session_token(user, {
|
||||
@@ -389,11 +443,15 @@ module.exports = eggspress(['/signup'], {
|
||||
// email confirmation
|
||||
//-------------------------------------------------------------
|
||||
// Email confirmation from signup is sent here
|
||||
if((!req.body.is_temp && email_confirmation_required) || user.requires_email_confirmation){
|
||||
if(req.body.send_confirmation_code || user.requires_email_confirmation)
|
||||
if ( (!req.body.is_temp && email_confirmation_required) || user.requires_email_confirmation ) {
|
||||
if ( req.body.send_confirmation_code || user.requires_email_confirmation )
|
||||
{
|
||||
send_email_verification_code(email_confirm_code, user.email);
|
||||
}
|
||||
else
|
||||
{
|
||||
send_email_verification_token(user.email_confirm_token, user.email, user.uuid);
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------
|
||||
@@ -419,7 +477,7 @@ module.exports = eggspress(['/signup'], {
|
||||
});
|
||||
|
||||
// add to mailchimp
|
||||
if(!req.body.is_temp){
|
||||
if ( !req.body.is_temp ) {
|
||||
const svc_event = Context.get('services').get('event');
|
||||
svc_event.emit('user.save_account', { user });
|
||||
}
|
||||
@@ -427,7 +485,7 @@ module.exports = eggspress(['/signup'], {
|
||||
// return results
|
||||
return res.send({
|
||||
token: token,
|
||||
user:{
|
||||
user: {
|
||||
username: user.username,
|
||||
uuid: user.uuid,
|
||||
email: user.email,
|
||||
@@ -436,6 +494,6 @@ module.exports = eggspress(['/signup'], {
|
||||
is_temp: (user.password === null && user.email === null),
|
||||
taskbar_items: await get_taskbar_items(user),
|
||||
referral_code,
|
||||
}
|
||||
})
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user