even more minor changes and reorganizing

This commit is contained in:
Evert Prants 2017-12-01 13:35:47 +02:00
parent e15dc7902c
commit f3d9435be0
Signed by: evert
GPG Key ID: 1688DA83D222D0B5
12 changed files with 62 additions and 50 deletions

View File

@ -1,6 +1,7 @@
import {EmailTemplate} from 'email-templates'
import path from 'path'
import nodemailer from 'nodemailer'
import config from '../../scripts/load-config'
const templateDir = path.join(__dirname, '../../', 'templates')

View File

@ -1,12 +1,13 @@
import qs from 'querystring'
import oauth from 'oauth-libre'
import uuidV1 from 'uuid/v1'
import crypto from 'crypto'
import config from '../../scripts/load-config'
import http from '../../scripts/http'
import models from './models'
import Image from './image'
import UAPI from './index'
import qs from 'querystring'
import oauth from 'oauth-libre'
import uuidV1 from 'uuid/v1'
import crypto from 'crypto'
const userFields = ['username', 'email', 'avatar_file', 'display_name', 'ip_address']
@ -74,7 +75,7 @@ const API = {
udataLimited.username = udataLimited.username.substring(0, 26)
// Check if the username is already taken
if (await UAPI.User.get(udataLimited.username) != null) {
if (await UAPI.User.get(udataLimited.username) != null || udataLimited.username.length < 4) {
udataLimited.username = udataLimited.username + UAPI.Hash(4)
}

View File

@ -1,8 +1,7 @@
import gm from 'gm'
import url from 'url'
import path from 'path'
import crypto from 'crypto'
import Promise from 'bluebird'
import uuid from 'uuid/v4'
import http from '../../scripts/http'
@ -17,10 +16,6 @@ const imageTypes = {
'image/jpeg': '.jpeg'
}
function imageUniquifier () {
return crypto.randomBytes(12).toString('hex')
}
function decodeBase64Image (dataString) {
let matches = dataString.match(/^data:([A-Za-z-+/]+);base64,(.+)$/)
let response = {}
@ -60,7 +55,7 @@ async function imageBase64 (baseObj) {
if (!imgData) return null
if (!imageTypes[imgData.type]) return null
let imageName = 'base64-' + imageUniquifier()
let imageName = 'base64-' + uuid()
let ext = imageTypes[imgData.type] || '.png'
imageName += ext
@ -81,7 +76,7 @@ async function downloadImage (imgUrl, designation) {
if (!imgUrl) return null
if (!designation) designation = 'download'
let imageName = designation + '-' + imageUniquifier()
let imageName = designation + '-' + uuid()
let uridata = url.parse(imgUrl)
let pathdata = path.parse(uridata.path)

View File

@ -1,15 +1,16 @@
import path from 'path'
import cprog from 'child_process'
import config from '../../scripts/load-config'
import http from '../../scripts/http'
import models from './models'
import crypto from 'crypto'
import notp from 'notp'
import base32 from 'thirty-two'
import emailer from './emailer'
import uuidV1 from 'uuid/v1'
import fs from 'fs-extra'
import config from '../../scripts/load-config'
import http from '../../scripts/http'
import models from './models'
import emailer from './emailer'
const emailRe = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
// Fork a bcrypt process to hash and compare passwords
@ -533,7 +534,6 @@ const API = {
console.debug('IPN Verified Notification:', body)
}
// TODO: add database field for this
if (body.txn_id) {
if (txnStore.indexOf(body.txn_id) !== -1) return true
txnStore.push(body.txn_id)

View File

@ -1,6 +1,7 @@
import crypto from 'crypto'
import API from './index'
import Model from './models'
import crypto from 'crypto'
const mAPI = {
getToken: async function (user) {

View File

@ -15,7 +15,7 @@ const args = {
function spawnWorkers () {
let workerCount = config.server.workers === 0 ? cpuCount : config.server.workers
console.log('Spinning up ' + workerCount + ' worker process' + (workerCount !== 1 ? 'es' : ''))
console.log('Spinning up %d worker process%s', workerCount, (workerCount !== 1 ? 'es' : ''))
for (let i = 0; i < workerCount; i++) {
spawnWorker()
@ -68,14 +68,14 @@ function spawnWorker (oldWorker) {
w.process.stderr.on('data', (data) => {
console.log(w.process.pid, data.toString().trim())
})
args.verbose && console.log('Starting worker process ' + w.process.pid + '...')
args.verbose && console.log('Starting worker process %d...', w.process.pid)
w.on('message', (message) => {
if (message === 'started') {
workers.push(w)
args.verbose && console.log('Started worker process ' + w.process.pid)
args.verbose && console.log('Started worker process', w.process.pid)
if (oldWorker) {
args.verbose && console.log('Stopping worker process ' + oldWorker.process.pid)
args.verbose && console.log('Stopping worker process', oldWorker.process.pid)
oldWorker.send('stop')
}
} else {
@ -99,7 +99,7 @@ cluster.setupMaster({
cluster.on('exit', (worker, code, signal) => {
let extra = ((code || '') + ' ' + (signal || '')).trim()
console.error('Worker process ' + worker.process.pid + ' exited ' + (extra ? '(' + extra + ')' : ''))
console.error('Worker process %d exited %s', worker.process.pid, (extra ? '(' + extra + ')' : ''))
let index = workers.indexOf(worker)

View File

@ -1,8 +1,9 @@
import express from 'express'
import ensureLogin from '../../scripts/ensureLogin'
import wrap from '../../scripts/asyncRoute'
import {User} from '../api'
import API from '../api/admin'
import {User} from '../api'
const router = express.Router()
const apiRouter = express.Router()

View File

@ -1,12 +1,13 @@
import express from 'express'
import RateLimit from 'express-rate-limit'
import multiparty from 'multiparty'
import config from '../../scripts/load-config'
import wrap from '../../scripts/asyncRoute'
import API from '../api'
import News from '../api/news'
import Image from '../api/image'
import APIExtern from '../api/external'
import Image from '../api/image'
import News from '../api/news'
import API from '../api'
let router = express.Router()
let dev = process.env.NODE_ENV !== 'production'

View File

@ -2,6 +2,7 @@ import fs from 'fs-extra'
import path from 'path'
import express from 'express'
import RateLimit from 'express-rate-limit'
import ensureLogin from '../../scripts/ensureLogin'
import config from '../../scripts/load-config'
import wrap from '../../scripts/asyncRoute'
@ -78,7 +79,7 @@ router.use(wrap(async (req, res, next) => {
}
// Update user session
let udata = await API.User.get(req.session.user.id)
let udata = await API.User.get(req.session.user)
setSession(req, udata)
// Update IP address
@ -158,6 +159,7 @@ router.get('/reset/:token', wrap(async (req, res) => {
router.get('/login', extraButtons, (req, res) => {
if (req.session.user) return redirectLogin(req, res)
if (req.query.returnTo) {
req.session.redirectUri = req.query.returnTo
}
@ -175,6 +177,21 @@ router.get('/register', extraButtons, formKeep, (req, res) => {
res.render('user/register')
})
// User activation endpoint (emailed link)
router.get('/activate/:token', wrap(async (req, res) => {
if (req.session.user) return res.redirect('/login')
let token = req.params.token
let success = await API.User.Login.activationToken(token)
if (!success) {
req.flash('message', {error: true, text: 'Invalid or expired activation token.'})
} else {
req.flash('message', {error: false, text: 'Your account has been activated! You may now log in.'})
}
res.redirect('/login')
}))
// View for enabling Two-Factor Authentication
router.get('/user/two-factor', ensureLogin, wrap(async (req, res) => {
let twoFaEnabled = await API.User.Login.totpTokenRequired(req.session.user)
@ -291,9 +308,11 @@ function formError (req, res, error, redirect) {
// Make sure characters are UTF-8
function cleanString (input) {
let output = ''
for (let i = 0; i < input.length; i++) {
output += input.charCodeAt(i) <= 127 ? input.charAt(i) : ''
}
return output
}
@ -309,6 +328,7 @@ function csrfValidation (req, res, next) {
// Enabling 2fa
router.post('/user/two-factor', csrfValidation, wrap(async (req, res, next) => {
if (!req.session.user) return next()
if (!req.body.code) {
return formError(req, res, 'You need to enter the code.')
}
@ -340,7 +360,9 @@ router.post('/user/two-factor/disable', csrfValidation, wrap(async (req, res, ne
// Verify 2FA for login
router.post('/login/verify', csrfValidation, wrap(async (req, res, next) => {
if (req.session.user) return next()
if (req.session.totp_check === null) return res.redirect('/login')
if (!req.body.code && !req.body.recovery) {
return formError(req, res, 'You need to enter the code.')
}
@ -360,6 +382,7 @@ router.post('/login/verify', csrfValidation, wrap(async (req, res, next) => {
// Log the user in. Limited resource
router.post('/login', accountLimiter, csrfValidation, wrap(async (req, res, next) => {
if (req.session.user) return next()
if (!req.body.username || !req.body.password || req.body.username === '') {
return res.redirect('/login')
}
@ -373,6 +396,7 @@ router.post('/login', accountLimiter, csrfValidation, wrap(async (req, res, next
if (!pwMatch) return formError(req, res, 'Invalid username or password.')
if (user.activated === 0) return formError(req, res, 'Please activate your account first.')
if (user.locked === 1) return formError(req, res, 'This account has been locked.')
// Check if the user is banned
@ -407,6 +431,7 @@ router.post('/login', accountLimiter, csrfValidation, wrap(async (req, res, next
// Password reset
router.post('/login/reset', accountLimiter, csrfValidation, wrap(async (req, res, next) => {
if (req.session.user) return next()
if (!req.body.email) {
return formError(req, res, 'You need to enter your email address.')
}
@ -420,7 +445,7 @@ router.post('/login/reset', accountLimiter, csrfValidation, wrap(async (req, res
try {
await API.User.Reset.reset(email)
req.flash('message', {error: false, text: 'We\'ve sent a link to your email address. Please check spam folders too!'})
req.flash('message', {error: false, text: 'We\'ve sent a link to your email address. Please check spam folders, too!'})
res.redirect('/login/reset?success=true')
} catch (e) {
return formError(req, res, e.message)
@ -466,6 +491,7 @@ router.post('/reset/:token', csrfValidation, wrap(async (req, res) => {
// Protected & Limited resource: Account registration
router.post('/register', accountLimiter, csrfValidation, wrap(async (req, res, next) => {
if (req.session.user) return next()
if (!req.body.username || !req.body.display_name || !req.body.password || !req.body.email) {
return formError(req, res, 'Please fill in all the fields.')
}
@ -563,7 +589,7 @@ router.post('/user/manage', csrfValidation, wrap(async (req, res, next) => {
let displayName = req.body.display_name
if (!displayName || !displayName.match(/^([^\\`]{3,32})$/i)) {
return formError(req, res, 'Invalid display name!')
return formError(req, res, 'Invalid Display Name!')
}
displayName = cleanString(displayName)
@ -680,7 +706,6 @@ router.post('/user/manage/email', accountLimiter, csrfValidation, wrap(async (re
return formError(req, res, e.message)
}
// TODO: Send necessary emails
console.warn('[SECURITY AUDIT] User \'%s\' email has been changed from %s', user.username, req.realIP)
req.session.user.email = newEmail
@ -790,21 +815,6 @@ router.get('/logout', (req, res) => {
res.redirect('/')
})
// User activation endpoint (emailed link)
router.get('/activate/:token', wrap(async (req, res) => {
if (req.session.user) return res.redirect('/login')
let token = req.params.token
let success = await API.User.Login.activationToken(token)
if (!success) {
req.flash('message', {error: true, text: 'Invalid or expired activation token.'})
} else {
req.flash('message', {error: false, text: 'Your account has been activated! You may now log in.'})
}
res.redirect('/login')
}))
router.use('/api', apiRouter)
router.use('/admin', adminRouter)
router.use('/mc', mcRouter)

View File

@ -1,4 +1,5 @@
import express from 'express'
import ensureLogin from '../../scripts/ensureLogin'
import wrap from '../../scripts/asyncRoute'
import Minecraft from '../api/minecraft'

View File

@ -1,5 +1,6 @@
import express from 'express'
import uapi from '../api'
import UAPI from '../api'
import OAuth2 from '../api/oauth2'
import RateLimit from 'express-rate-limit'
import wrap from '../../scripts/asyncRoute'
@ -31,7 +32,7 @@ router.post('/introspect', oauth.controller.introspection)
// Protected user information resource
router.get('/user', oauth.bearer, wrap(async (req, res) => {
let accessToken = req.oauth2.accessToken
let user = await uapi.User.get(accessToken.user_id)
let user = await UAPI.User.get(accessToken.user_id)
if (!user) {
return res.status(404).jsonp({

View File

@ -68,7 +68,7 @@ module.exports = (args) => {
app.set('views', path.join(__dirname, '../views'))
if (args.dev) {
console.log('Worker is in development mode')
console.warn('Worker is in development mode')
// Dev logger
const morgan = require('morgan')