2018-07-25 01:45:05 -04:00
|
|
|
const express = require('express');
|
|
|
|
const router = express.Router();
|
2018-07-26 17:34:47 -04:00
|
|
|
const config = require('config');
|
2018-12-26 19:02:59 -05:00
|
|
|
const fs = require('fs').promises;
|
2019-01-02 15:20:53 -05:00
|
|
|
const passport = require('passport');
|
|
|
|
|
|
|
|
const canonicalize = require('../../util/auth/canonicalize');
|
2018-01-15 15:29:32 -05:00
|
|
|
|
2018-08-01 11:54:35 -04:00
|
|
|
const ModelPath = '../../models/';
|
2018-07-26 19:40:42 -04:00
|
|
|
const User = require(ModelPath + 'User.js');
|
|
|
|
const Invite = require(ModelPath + 'Invite.js');
|
|
|
|
|
|
|
|
|
2018-08-01 11:54:35 -04:00
|
|
|
const requireAuth = require('../../util/auth').requireAuth;
|
2019-01-02 14:20:10 -05:00
|
|
|
const verifyBody = require('../../util/verifyBody');
|
2019-01-02 15:20:53 -05:00
|
|
|
const rateLimit = require('../../util/rateLimit');
|
2018-07-25 01:45:05 -04:00
|
|
|
|
2018-07-25 18:45:38 -04:00
|
|
|
// Wraps passport.authenticate to return a promise
|
2018-07-28 18:22:48 -04:00
|
|
|
const authenticate = (req, res, next) => {
|
2018-07-25 18:45:38 -04:00
|
|
|
return new Promise((resolve) => {
|
|
|
|
passport.authenticate('local', (err, user) => {
|
|
|
|
resolve(user);
|
|
|
|
})(req, res, next);
|
|
|
|
});
|
2018-07-28 18:22:48 -04:00
|
|
|
};
|
2018-07-25 01:45:05 -04:00
|
|
|
|
2019-01-02 15:20:53 -05:00
|
|
|
// Wraps passport session creation to return a promise
|
2018-07-28 18:22:48 -04:00
|
|
|
const login = (user, req) => {
|
2018-07-25 18:45:38 -04:00
|
|
|
return new Promise((resolve) => {
|
|
|
|
req.login(user, resolve);
|
|
|
|
});
|
2018-07-28 18:22:48 -04:00
|
|
|
};
|
2017-10-12 12:50:02 -04:00
|
|
|
|
2019-01-02 15:20:53 -05:00
|
|
|
const registerParams = [
|
|
|
|
{name: 'displayname', type: 'string', maxLength: config.get('User.Username.maxLength'), sanitize: true, restrict: new RegExp(config.get('User.Username.restrictedChars'))},
|
2018-07-28 18:22:48 -04:00
|
|
|
{name: 'password', type: 'string'},
|
|
|
|
{name: 'invite', type: 'string'}];
|
2019-01-02 15:20:53 -05:00
|
|
|
|
2018-07-28 18:22:48 -04:00
|
|
|
router.post('/register',
|
2019-01-02 15:20:53 -05:00
|
|
|
rateLimit(config.get('RateLimit.register.window'), config.get('RateLimit.register.max'), true),
|
|
|
|
verifyBody(registerParams),
|
|
|
|
async (req, res) => {
|
|
|
|
const username = canonicalize(req.body.displayname);
|
|
|
|
|
|
|
|
// Retrieve invite and username status
|
|
|
|
const [invite, usernameCount] = await Promise.all([
|
|
|
|
Invite.findOne({code: req.body.invite}),
|
|
|
|
User.countDocuments({username: username})
|
|
|
|
]);
|
|
|
|
|
|
|
|
// Validate the invite
|
|
|
|
if (!invite)
|
|
|
|
return res.status(422).json({message: 'Invalid invite code.'});
|
|
|
|
if (invite.used)
|
|
|
|
return res.status(422).json({message: 'Invite already used.'});
|
|
|
|
if (invite.expires != null && invite.expires < Date.now())
|
|
|
|
return res.status(422).json({message: 'Invite expired.'});
|
|
|
|
|
|
|
|
// Validate the username
|
|
|
|
if (usernameCount !== 0)
|
|
|
|
return res.status(422).json({message: 'Username in use.'});
|
|
|
|
|
|
|
|
// Create the user object
|
|
|
|
await User.register({
|
|
|
|
username: username,
|
2018-07-26 19:40:42 -04:00
|
|
|
displayname: req.body.displayname,
|
2019-01-02 15:20:53 -05:00
|
|
|
scope: invite.scope,
|
2018-07-25 18:45:38 -04:00
|
|
|
date: Date.now()
|
2019-01-02 15:20:53 -05:00
|
|
|
}, req.body.password);
|
2018-07-25 01:45:05 -04:00
|
|
|
|
2019-01-02 15:20:53 -05:00
|
|
|
// Update the invite as used
|
|
|
|
await Invite.updateOne({code: invite.code}, {recipient: username, used: Date.now()});
|
2017-10-11 10:15:19 -04:00
|
|
|
|
2019-01-02 15:20:53 -05:00
|
|
|
res.status(200).json({'message': 'Registration successful.'});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const loginParams = [
|
|
|
|
{name: 'displayname', type: 'string'},
|
2018-07-29 11:00:14 -04:00
|
|
|
{name: 'password', type: 'string'}];
|
2019-01-02 15:20:53 -05:00
|
|
|
|
2018-12-26 20:47:04 -05:00
|
|
|
router.post('/login',
|
2019-01-02 15:20:53 -05:00
|
|
|
rateLimit(config.get('RateLimit.login.window'), config.get('RateLimit.login.max'), true),
|
|
|
|
verifyBody(loginParams),
|
2019-01-02 14:23:43 -05:00
|
|
|
async (req, res, next) => {
|
2019-01-02 15:20:53 -05:00
|
|
|
req.body.username = canonicalize(req.body.displayname);
|
|
|
|
|
|
|
|
// Authenticate
|
|
|
|
const user = await authenticate(req, res, next);
|
|
|
|
if (!user) {
|
|
|
|
// Log failure
|
|
|
|
await fs.appendFile('auth.log', `${new Date().toISOString()} login ${req.ip}\n`);
|
|
|
|
return res.status(401).json({'message': 'Unauthorized.'});
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create session
|
|
|
|
await login(user, req);
|
|
|
|
|
|
|
|
// Set session vars
|
|
|
|
req.session.passport.displayname = user.displayname;
|
|
|
|
req.session.passport.scope = user.scope;
|
|
|
|
|
|
|
|
res.status(200).json({'message': 'Logged in.'});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
2017-10-11 10:15:19 -04:00
|
|
|
|
2019-01-02 14:23:43 -05:00
|
|
|
router.post('/logout', (req, res) => {
|
2018-07-28 12:53:49 -04:00
|
|
|
if (!req.isAuthenticated())
|
|
|
|
return res.status(400).json({message: 'Not logged in.'});
|
|
|
|
|
2017-10-18 13:31:08 -04:00
|
|
|
req.logout();
|
|
|
|
res.status(200).json({'message': 'Logged out.'});
|
2017-10-14 17:49:11 -04:00
|
|
|
});
|
|
|
|
|
2019-01-02 15:20:53 -05:00
|
|
|
|
|
|
|
|
2018-07-26 13:15:54 -04:00
|
|
|
router.get('/whoami', requireAuth(), (req, res) => {
|
2018-07-25 18:45:38 -04:00
|
|
|
res.status(200).json({
|
2018-08-12 05:30:50 -04:00
|
|
|
username: req.username,
|
|
|
|
displayname: req.displayname,
|
2018-07-26 19:40:42 -04:00
|
|
|
scope: req.scope,
|
|
|
|
key: req.key
|
2018-07-25 18:45:38 -04:00
|
|
|
});
|
2017-10-18 13:31:08 -04:00
|
|
|
});
|
2017-10-11 10:15:19 -04:00
|
|
|
|
|
|
|
module.exports = router;
|