Refactoring
This commit is contained in:
parent
3eb97bab6a
commit
5b6f86668a
70
src/controller/account.js
Normal file
70
src/controller/account.js
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
import { InvalidRequestError } from '../errors';
|
||||||
|
import { isValidEmail } from '../utilities';
|
||||||
|
import { parse as parseURL } from 'url';
|
||||||
|
import bcrypt from 'bcrypt';
|
||||||
|
import Promise from 'bluebird';
|
||||||
|
|
||||||
|
Promise.promisifyAll(bcrypt);
|
||||||
|
|
||||||
|
class AccountController {
|
||||||
|
constructor(accountDB, globalMessageBus) {
|
||||||
|
this.accountDB = accountDB;
|
||||||
|
this.globalMessageBus = globalMessageBus;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getAccount(name) {
|
||||||
|
const user = await this.accountDB.getByName(name);
|
||||||
|
|
||||||
|
if (user) {
|
||||||
|
return {
|
||||||
|
name: user.name,
|
||||||
|
email: user.email,
|
||||||
|
profile: user.profile,
|
||||||
|
time: user.time
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateAccount(name, updates, password = null) {
|
||||||
|
let requirePassword = false;
|
||||||
|
const fields = {};
|
||||||
|
|
||||||
|
if (!updates || updates.toString() !== '[object Object]') {
|
||||||
|
throw new InvalidRequestError('Malformed input');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (updates.email) {
|
||||||
|
if (!isValidEmail(updates.email)) {
|
||||||
|
throw new InvalidRequestError('Invalid email address');
|
||||||
|
}
|
||||||
|
|
||||||
|
fields.email = updates.email;
|
||||||
|
requirePassword = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (requirePassword) {
|
||||||
|
if (!password) {
|
||||||
|
throw new InvalidRequestError('Password required');
|
||||||
|
}
|
||||||
|
|
||||||
|
const user = await this.accountDB.getUserByName(name);
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
throw new InvalidRequestError('User does not exist');
|
||||||
|
}
|
||||||
|
|
||||||
|
// For legacy reasons, the password was truncated to 100 chars.
|
||||||
|
password = password.substring(0, 100);
|
||||||
|
|
||||||
|
if (!await bcrypt.compareAsync(password, user.password)) {
|
||||||
|
throw new InvalidRequestError('Invalid password');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.accountDB.updateByName(name, fields);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export { AccountController };
|
|
@ -54,6 +54,7 @@ import * as Switches from './switches';
|
||||||
import { Gauge } from 'prom-client';
|
import { Gauge } from 'prom-client';
|
||||||
import { AccountDB } from './db/account';
|
import { AccountDB } from './db/account';
|
||||||
import { ChannelDB } from './db/channel';
|
import { ChannelDB } from './db/channel';
|
||||||
|
import { AccountController } from './controller/account';
|
||||||
|
|
||||||
var Server = function () {
|
var Server = function () {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
@ -88,6 +89,8 @@ var Server = function () {
|
||||||
const accountDB = new AccountDB(db.getDB());
|
const accountDB = new AccountDB(db.getDB());
|
||||||
const channelDB = new ChannelDB(db.getDB());
|
const channelDB = new ChannelDB(db.getDB());
|
||||||
|
|
||||||
|
const accountController = new AccountController(accountDB, globalMessageBus);
|
||||||
|
|
||||||
// webserver init -----------------------------------------------------
|
// webserver init -----------------------------------------------------
|
||||||
const ioConfig = IOConfiguration.fromOldConfig(Config);
|
const ioConfig = IOConfiguration.fromOldConfig(Config);
|
||||||
const webConfig = WebConfiguration.fromOldConfig(Config);
|
const webConfig = WebConfiguration.fromOldConfig(Config);
|
||||||
|
@ -108,7 +111,7 @@ var Server = function () {
|
||||||
channelIndex,
|
channelIndex,
|
||||||
session,
|
session,
|
||||||
globalMessageBus,
|
globalMessageBus,
|
||||||
accountDB,
|
accountController,
|
||||||
channelDB);
|
channelDB);
|
||||||
|
|
||||||
// http/https/sio server init -----------------------------------------
|
// http/https/sio server init -----------------------------------------
|
||||||
|
|
|
@ -53,6 +53,10 @@
|
||||||
},
|
},
|
||||||
|
|
||||||
root.isValidEmail = function (email) {
|
root.isValidEmail = function (email) {
|
||||||
|
if (typeof email !== "string") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (email.length > 255) {
|
if (email.length > 255) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,8 +75,8 @@ function reportError(req, res, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
class AccountDataRoute {
|
class AccountDataRoute {
|
||||||
constructor(accountDB, channelDB, csrfVerify, verifySessionAsync) {
|
constructor(accountController, channelDB, csrfVerify, verifySessionAsync) {
|
||||||
this.accountDB = accountDB;
|
this.accountController = accountController;
|
||||||
this.channelDB = channelDB;
|
this.channelDB = channelDB;
|
||||||
this.csrfVerify = csrfVerify;
|
this.csrfVerify = csrfVerify;
|
||||||
this.verifySessionAsync = verifySessionAsync;
|
this.verifySessionAsync = verifySessionAsync;
|
||||||
|
@ -88,22 +88,9 @@ class AccountDataRoute {
|
||||||
if (!await authorize(req, res, this.csrfVerify, this.verifySessionAsync)) return;
|
if (!await authorize(req, res, this.csrfVerify, this.verifySessionAsync)) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const user = await this.accountDB.getByName(req.params.user);
|
const user = await this.accountController.getAccount(req.params.user);
|
||||||
|
|
||||||
if (user) {
|
res.status(user === null ? 404 : 200).json({ result: user });
|
||||||
// Whitelist fields to expose, to avoid accidental
|
|
||||||
// information leaks when new fields are added.
|
|
||||||
const result = {
|
|
||||||
name: user.name,
|
|
||||||
email: user.email,
|
|
||||||
profile: user.profile,
|
|
||||||
time: user.time
|
|
||||||
};
|
|
||||||
|
|
||||||
res.status(200).json({ result });
|
|
||||||
} else {
|
|
||||||
res.status(404).json({ result: null });
|
|
||||||
}
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
reportError(req, res, error);
|
reportError(req, res, error);
|
||||||
}
|
}
|
||||||
|
@ -114,7 +101,14 @@ class AccountDataRoute {
|
||||||
if (!checkAcceptsJSON(req, res)) return;
|
if (!checkAcceptsJSON(req, res)) return;
|
||||||
if (!await authorize(req, res, this.csrfVerify, this.verifySessionAsync)) return;
|
if (!await authorize(req, res, this.csrfVerify, this.verifySessionAsync)) return;
|
||||||
|
|
||||||
res.status(501).json({ error: 'Not implemented' });
|
const { password, updates } = req.body;
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.accountController.updateAccount(req.user, updates, password);
|
||||||
|
res.status(204).send();
|
||||||
|
} catch (error) {
|
||||||
|
reportError(req, res, error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@GET('/account/data/:user/channels')
|
@GET('/account/data/:user/channels')
|
||||||
|
|
|
@ -193,7 +193,7 @@ module.exports = {
|
||||||
channelIndex,
|
channelIndex,
|
||||||
session,
|
session,
|
||||||
globalMessageBus,
|
globalMessageBus,
|
||||||
accountDB,
|
accountController,
|
||||||
channelDB
|
channelDB
|
||||||
) {
|
) {
|
||||||
patchExpressToHandleAsync();
|
patchExpressToHandleAsync();
|
||||||
|
@ -209,6 +209,9 @@ module.exports = {
|
||||||
extended: false,
|
extended: false,
|
||||||
limit: '1kb' // No POST data should ever exceed this size under normal usage
|
limit: '1kb' // No POST data should ever exceed this size under normal usage
|
||||||
}));
|
}));
|
||||||
|
app.use(bodyParser.json({
|
||||||
|
limit: '1kb'
|
||||||
|
}));
|
||||||
if (webConfig.getCookieSecret() === 'change-me') {
|
if (webConfig.getCookieSecret() === 'change-me') {
|
||||||
LOGGER.warn('The configured cookie secret was left as the ' +
|
LOGGER.warn('The configured cookie secret was left as the ' +
|
||||||
'default of "change-me".');
|
'default of "change-me".');
|
||||||
|
@ -261,7 +264,12 @@ module.exports = {
|
||||||
const { AccountDataRoute } = require('./routes/account/data');
|
const { AccountDataRoute } = require('./routes/account/data');
|
||||||
require('@calzoneman/express-babel-decorators').bind(
|
require('@calzoneman/express-babel-decorators').bind(
|
||||||
app,
|
app,
|
||||||
new AccountDataRoute(accountDB, channelDB, csrfVerify, verifySessionAsync)
|
new AccountDataRoute(
|
||||||
|
accountController,
|
||||||
|
channelDB,
|
||||||
|
csrfVerify,
|
||||||
|
verifySessionAsync
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ const sinon = require('sinon');
|
||||||
const express = require('express');
|
const express = require('express');
|
||||||
const { AccountDB } = require('../../../../lib/db/account');
|
const { AccountDB } = require('../../../../lib/db/account');
|
||||||
const { ChannelDB } = require('../../../../lib/db/channel');
|
const { ChannelDB } = require('../../../../lib/db/channel');
|
||||||
|
const { AccountController } = require('../../../../lib/controller/account');
|
||||||
const { AccountDataRoute } = require('../../../../lib/web/routes/account/data');
|
const { AccountDataRoute } = require('../../../../lib/web/routes/account/data');
|
||||||
const http = require('http');
|
const http = require('http');
|
||||||
const expressBabelDecorators = require('@calzoneman/express-babel-decorators');
|
const expressBabelDecorators = require('@calzoneman/express-babel-decorators');
|
||||||
|
@ -10,6 +11,7 @@ const nodeurl = require('url');
|
||||||
const Promise = require('bluebird');
|
const Promise = require('bluebird');
|
||||||
const bodyParser = require('body-parser');
|
const bodyParser = require('body-parser');
|
||||||
const { CSRFError } = require('../../../../lib/errors');
|
const { CSRFError } = require('../../../../lib/errors');
|
||||||
|
const { EventEmitter } = require('events');
|
||||||
|
|
||||||
const TEST_PORT = 10111;
|
const TEST_PORT = 10111;
|
||||||
const URL_BASE = `http://localhost:${TEST_PORT}`;
|
const URL_BASE = `http://localhost:${TEST_PORT}`;
|
||||||
|
@ -89,7 +91,7 @@ describe('AccountDataRoute', () => {
|
||||||
}));
|
}));
|
||||||
|
|
||||||
accountDataRoute = new AccountDataRoute(
|
accountDataRoute = new AccountDataRoute(
|
||||||
realAccountDB,
|
new AccountController(realAccountDB, new EventEmitter()),
|
||||||
realChannelDB,
|
realChannelDB,
|
||||||
csrfVerify,
|
csrfVerify,
|
||||||
verifySessionAsync
|
verifySessionAsync
|
||||||
|
|
Loading…
Reference in a new issue