hostr/web/lib/auth.js

225 lines
5.5 KiB
JavaScript
Raw Normal View History

2015-07-09 23:01:43 +01:00
import crypto from 'crypto';
2016-06-19 10:14:47 -07:00
import { join } from 'path';
2015-07-09 23:01:43 +01:00
import passwords from 'passwords';
import uuid from 'node-uuid';
import views from 'co-views';
2016-06-19 10:14:47 -07:00
import models from '../../models';
2016-06-06 15:37:00 +01:00
const render = views(join(__dirname, '..', 'views'), { default: 'ejs' });
2015-07-09 23:01:43 +01:00
import debugname from 'debug';
const debug = debugname('hostr-web:auth');
2016-05-01 12:30:46 +01:00
import sendgridInit from 'sendgrid';
const sendgrid = sendgridInit(process.env.SENDGRID_KEY);
2015-07-09 23:01:43 +01:00
2016-06-06 15:37:00 +01:00
const from = process.env.EMAIL_FROM;
const fromname = process.env.EMAIL_NAME;
2015-08-22 16:16:15 +01:00
export function* authenticate(email, password) {
const remoteIp = this.headers['x-real-ip'] || this.ip;
2015-07-09 23:01:43 +01:00
2015-08-23 22:12:32 +01:00
if (!password || password.length < 6) {
2015-07-09 23:01:43 +01:00
debug('No password, or password too short');
return new Error('Invalid login details');
}
2016-06-19 10:14:47 -07:00
const count = yield models.login.count({
where: {
ip: remoteIp,
successful: false,
createdAt: {
2016-08-07 14:38:05 +01:00
$gt: Math.ceil(Date.now()) - 600000,
2016-06-19 10:14:47 -07:00
},
},
2016-06-06 15:37:00 +01:00
});
2016-06-19 10:14:47 -07:00
2015-07-09 23:01:43 +01:00
if (count > 25) {
debug('Throttling brute force');
return new Error('Invalid login details');
}
2016-06-19 10:14:47 -07:00
const user = yield models.user.findOne({
2016-08-07 14:38:05 +01:00
where: {
email: email.toLowerCase(),
activated: true,
},
2016-06-19 10:14:47 -07:00
});
2016-08-07 14:38:05 +01:00
debug(user);
2016-06-19 10:14:47 -07:00
const login = yield models.login.create({
ip: remoteIp,
successful: false,
2016-06-06 15:37:00 +01:00
});
2016-06-19 10:14:47 -07:00
2016-08-07 14:38:05 +01:00
if (user && user.password) {
2016-06-19 10:14:47 -07:00
if (yield passwords.verify(password, user.password)) {
2015-07-09 23:01:43 +01:00
debug('Password verified');
login.successful = true;
2016-06-19 10:14:47 -07:00
yield login.save();
2016-08-07 14:38:05 +01:00
debug(user);
2015-07-09 23:01:43 +01:00
return user;
}
2015-08-23 22:12:32 +01:00
debug('Password invalid');
2016-06-19 10:14:47 -07:00
login.userId = user.id;
2015-07-09 23:01:43 +01:00
}
2016-06-19 10:14:47 -07:00
yield login.save();
2016-08-07 14:38:05 +01:00
return false;
2015-07-09 23:01:43 +01:00
}
2015-08-22 16:16:15 +01:00
export function* setupSession(user) {
2015-07-09 23:01:43 +01:00
debug('Setting up session');
const token = uuid.v4();
2016-06-19 10:14:47 -07:00
yield this.redis.set(token, user.id, 'EX', 604800);
2015-07-09 23:01:43 +01:00
const sessionUser = {
2016-06-19 10:14:47 -07:00
id: user.id,
2016-06-06 15:37:00 +01:00
email: user.email,
dailyUploadAllowance: 15,
maxFileSize: 20971520,
2016-06-19 10:14:47 -07:00
joined: user.createdAt,
plan: user.plan,
uploadsToday: yield models.file.count({ userId: user.id }),
2016-06-06 15:37:00 +01:00
md5: crypto.createHash('md5').update(user.email).digest('hex'),
token,
2015-07-09 23:01:43 +01:00
};
if (sessionUser.plan === 'Pro') {
sessionUser.maxFileSize = 524288000;
sessionUser.dailyUploadAllowance = 'unlimited';
}
2015-08-22 16:16:15 +01:00
this.session.user = sessionUser;
if (this.request.body.remember && this.request.body.remember === 'on') {
2016-06-19 10:14:47 -07:00
const remember = yield models.remember.create({
id: uuid(),
userId: user.id,
});
this.cookies.set('r', remember.id, { maxAge: 1209600000, httpOnly: true });
2015-07-09 23:01:43 +01:00
}
debug('Session set up');
}
2015-08-22 16:16:15 +01:00
export function* signup(email, password, ip) {
2016-08-07 14:38:05 +01:00
const existingUser = yield models.user.findOne({
where: {
email,
activated: true,
},
});
2015-07-09 23:01:43 +01:00
if (existingUser) {
debug('Email already in use.');
2015-08-23 16:50:40 +01:00
throw new Error('Email already in use.');
2015-07-09 23:01:43 +01:00
}
const cryptedPassword = yield passwords.crypt(password);
2016-06-19 10:14:47 -07:00
const user = yield models.user.create({
2016-06-06 15:37:00 +01:00
email,
2016-06-19 10:14:47 -07:00
password: cryptedPassword,
ip,
2016-08-07 14:38:05 +01:00
plan: 'Free',
2016-06-19 10:14:47 -07:00
activation: {
id: uuid(),
email,
},
}, {
include: [models.activation],
});
yield user.save();
2015-07-09 23:01:43 +01:00
2016-06-06 15:37:00 +01:00
const html = yield render('email/inlined/activate', {
2016-06-19 10:14:47 -07:00
activationUrl: `${process.env.WEB_BASE_URL}/activate/${user.activation.id}`,
2016-06-06 15:37:00 +01:00
});
2015-07-09 23:01:43 +01:00
const text = `Thanks for signing up to Hostr!
Please confirm your email address by clicking the link below.
2016-06-19 10:14:47 -07:00
${process.env.WEB_BASE_URL}/activate/${user.activation.id}
2015-07-09 23:01:43 +01:00
Jonathan Cremin, Hostr Founder
`;
2016-05-01 12:30:46 +01:00
const mail = new sendgrid.Email({
to: user.email,
2015-07-09 23:01:43 +01:00
subject: 'Welcome to Hostr',
2016-06-06 15:37:00 +01:00
from,
fromname,
html,
text,
2016-05-01 12:30:46 +01:00
});
mail.addCategory('activate');
sendgrid.send(mail);
2015-07-09 23:01:43 +01:00
}
2015-08-22 16:16:15 +01:00
export function* sendResetToken(email) {
2016-08-07 14:38:05 +01:00
const user = yield models.user.findOne({
where: {
email,
},
});
2015-07-09 23:01:43 +01:00
if (user) {
2016-06-19 10:14:47 -07:00
const reset = yield models.reset.create({
id: uuid.v4(),
userId: user.id,
2016-06-06 15:37:00 +01:00
});
const html = yield render('email/inlined/forgot', {
2016-06-19 10:14:47 -07:00
forgotUrl: `${process.env.WEB_BASE_URL}/forgot/${reset.id}`,
2015-07-09 23:01:43 +01:00
});
const text = `It seems you've forgotten your password :(
2016-06-19 10:14:47 -07:00
Visit ${process.env.WEB_BASE_URL}/forgot/${reset.id} to set a new one.
2015-07-09 23:01:43 +01:00
`;
2016-05-01 12:30:46 +01:00
const mail = new sendgrid.Email({
to: user.email,
from: 'jonathan@hostr.co',
fromname: 'Jonathan from Hostr',
2015-07-09 23:01:43 +01:00
subject: 'Hostr Password Reset',
2016-06-06 15:37:00 +01:00
html,
text,
2016-05-01 12:30:46 +01:00
});
mail.addCategory('password-reset');
sendgrid.send(mail);
2015-07-09 23:01:43 +01:00
} else {
2015-08-23 16:50:40 +01:00
throw new Error('There was an error looking up your email address.');
2015-07-09 23:01:43 +01:00
}
}
2015-08-22 16:16:15 +01:00
export function* fromToken(token) {
2016-06-19 10:14:47 -07:00
const userId = yield this.redis.get(token);
2016-08-07 14:38:05 +01:00
return yield models.user.findById(userId);
2015-07-09 23:01:43 +01:00
}
2016-06-19 10:14:47 -07:00
export function* fromCookie(rememberId) {
const userId = yield models.remember.findById(rememberId);
2016-08-07 14:38:05 +01:00
return yield models.user.findById(userId);
2015-07-09 23:01:43 +01:00
}
2016-06-19 10:14:47 -07:00
export function* validateResetToken(resetId) {
2016-08-07 14:38:05 +01:00
return yield models.reset.findById(resetId);
2015-07-09 23:01:43 +01:00
}
2015-08-22 16:16:15 +01:00
export function* updatePassword(userId, password) {
2015-07-09 23:01:43 +01:00
const cryptedPassword = yield passwords.crypt(password);
2016-06-19 10:14:47 -07:00
const user = yield models.user.findById(userId);
user.password = cryptedPassword;
yield user.save();
2015-07-09 23:01:43 +01:00
}
2015-08-22 16:16:15 +01:00
export function* activateUser(code) {
2016-06-19 10:14:47 -07:00
debug(code);
const activation = yield models.activation.findOne({
where: {
id: code,
},
});
if (activation.updatedAt.getTime() === activation.createdAt.getTime()) {
activation.activated = true;
yield activation.save();
const user = yield activation.getUser();
user.activated = true;
yield user.save();
2015-09-04 00:04:48 +02:00
yield setupSession.call(this, user);
return true;
2015-07-09 23:01:43 +01:00
}
2015-09-04 00:04:48 +02:00
return false;
2015-07-09 23:01:43 +01:00
}