hostr/web/lib/auth.js

194 lines
5.7 KiB
JavaScript
Raw Normal View History

2015-07-09 23:01:43 +01:00
import crypto from 'crypto';
import passwords from 'passwords';
import uuid from 'node-uuid';
import views from 'co-views';
const render = views('views', { default: 'ejs'});
import debugname from 'debug';
const debug = debugname('hostr-web:auth');
import { Mandrill } from 'mandrill-api/mandrill';
const mandrill = new Mandrill(process.env.MANDRILL_KEY);
export function* authenticate(ctx, email, password) {
const Users = ctx.db.Users;
const Logins = ctx.db.Logins;
const remoteIp = ctx.headers['x-real-ip'] || ctx.ip;
if (!password || password.length < 6){
debug('No password, or password too short');
return new Error('Invalid login details');
}
const count = yield Logins.count({ip: remoteIp, successful: false, at: { '$gt': Math.ceil(Date.now() / 1000) - 600}});
if (count > 25) {
debug('Throttling brute force');
return new Error('Invalid login details');
}
const login = {ip: remoteIp, at: Math.ceil(Date.now() / 1000), successful: null};
yield Logins.save(login);
const user = yield Users.findOne({email: email.toLowerCase(), banned: {'$exists': false}, status: {'$ne': 'deleted'}});
if (user) {
const verified = yield passwords.verify(password, user.salted_password);
if (verified) {
debug('Password verified');
login.successful = true;
yield Logins.updateOne({_id: login._id}, login);
return user;
} else {
debug('Password invalid');
login.successful = false;
yield Logins.updateOne({_id: login._id}, login);
}
} else {
debug('Email invalid');
login.successful = false;
yield Logins.updateOne({_id: login._id}, login);
}
}
export function* setupSession(ctx, user) {
debug('Setting up session');
const token = uuid.v4();
yield ctx.redis.set(token, user._id, 'EX', 604800);
const sessionUser = {
'id': user._id,
'email': user.email,
'dailyUploadAllowance': 15,
'maxFileSize': 20971520,
'joined': user.joined,
'plan': user.type || 'Free',
'uploadsToday': 0,
'token': token,
'md5': crypto.createHash('md5').update(user.email).digest('hex')
};
if (sessionUser.plan === 'Pro') {
sessionUser.maxFileSize = 524288000;
sessionUser.dailyUploadAllowance = 'unlimited';
}
ctx.session.user = sessionUser;
if (ctx.request.body.remember && ctx.request.body.remember === 'on') {
const Remember = ctx.db.Remember;
var rememberToken = uuid();
Remember.save({_id: rememberToken, 'user_id': user.id, created: new Date().getTime()});
ctx.cookies.set('r', rememberToken, { maxAge: 1209600000, httpOnly: true});
}
debug('Session set up');
}
export function* signup(ctx, email, password, ip) {
const Users = ctx.db.Users;
const existingUser = yield Users.findOne({email: email, status: {'$ne': 'deleted'}});
if (existingUser) {
debug('Email already in use.');
return 'Email already in use.';
}
const cryptedPassword = yield passwords.crypt(password);
var user = {
email: email,
'salted_password': cryptedPassword,
joined: Math.round(new Date().getTime() / 1000),
'signup_ip': ip,
activationCode: uuid()
};
Users.insertOne(user);
const html = yield render('email/inlined/activate', {activationUrl: process.env.BASE_URL + '/activate/' + user.activationCode});
const text = `Thanks for signing up to Hostr!
Please confirm your email address by clicking the link below.
${process.env.BASE_URL + '/activate/' + user.activationCode}
Jonathan Cremin, Hostr Founder
`;
mandrill.messages.send({message: {
html: html,
text: text,
subject: 'Welcome to Hostr',
'from_email': 'jonathan@hostr.co',
'from_name': 'Jonathan from Hostr',
to: [{
email: user.email,
type: 'to'
}],
'tags': [
'user-activation'
]
}});
}
export function* sendResetToken(ctx, email) {
const Users = ctx.db.Users;
const Reset = ctx.db.Reset;
const user = yield Users.findOne({email: email});
if (user) {
var token = uuid.v4();
Reset.save({
'_id': user._id,
'token': token,
'created': Math.round(new Date().getTime() / 1000)
});
const html = yield this.render('email/inlined/forgot', {forgotUrl: this.locals.baseUrl + '/forgot/' + token});
const text = `It seems you've forgotten your password :(
Visit ${ctx.locals.baseUrl + '/forgot/' + token} to set a new one.
`;
mandrill.messages.send({message: {
html: html,
text: text,
subject: 'Hostr Password Reset',
'from_email': 'jonathan@hostr.co',
'from_name': 'Jonathan from Hostr',
to: [{
email: user.email,
type: 'to'
}],
'tags': [
'password-reset'
]
}});
} else {
return 'There was an error looking up your email address.';
}
}
export function* fromToken(ctx, token) {
const Users = ctx.db.Users;
const reply = yield ctx.redis.get(token);
return yield Users.findOne({_id: reply});
}
export function* fromCookie(ctx, cookie) {
const Remember = ctx.db.Remember;
const Users = ctx.db.Users;
const remember = yield Remember.findOne({_id: cookie});
return yield Users.findOne({_id: remember.user_id});
}
export function* validateResetToken(ctx) {
const Reset = ctx.db.Reset;
return yield Reset.findOne({token: ctx.params.id});
}
export function* updatePassword(ctx, userId, password) {
const Users = ctx.db.Users;
const cryptedPassword = yield passwords.crypt(password);
yield Users.update({_id: userId}, {'$set': {'salted_password': cryptedPassword}});
}
export function* activateUser(ctx, code) {
const Users = ctx.db.Users;
const user = yield Users.findOne({activationCode: code});
if (user) {
Users.updateOne({_id: user._id}, {'$unset': {activationCode: ''}});
yield setupSession(ctx, user);
}
}