| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247 | 'use strict';const coreClass = require("../core");const express = require('express');const bodyParser = require('body-parser');const cookieParser = require('cookie-parser');const cors = require('cors');const config = require('config');const async = require('async');const request = require('request');const OAuth2 = require('oauth').OAuth2;module.exports = class extends coreClass {	initialize() {		return new Promise((resolve, reject) => {			this.setStage(1);			const 	logger 	= this.logger,					mail	= this.moduleManager.modules["mail"],					cache	= this.moduleManager.modules["cache"],					db		= this.moduleManager.modules["db"];						this.utils = this.moduleManager.modules["utils"];			let app = this.app = express();			const SIDname = config.get("cookie.SIDname");			this.server = app.listen(config.get('serverPort'));			app.use(cookieParser());			app.use(bodyParser.json());			app.use(bodyParser.urlencoded({ extended: true }));			let corsOptions = Object.assign({}, config.get('cors'));			app.use(cors(corsOptions));			app.options('*', cors(corsOptions));			let oauth2 = new OAuth2(				config.get('apis.github.client'),				config.get('apis.github.secret'),				'https://github.com/',				'login/oauth/authorize',				'login/oauth/access_token',				null			);			let redirect_uri = config.get('serverDomain') + '/auth/github/authorize/callback';			app.get('/auth/github/authorize', async (req, res) => {				try { await this._validateHook(); } catch { return; }				let params = [					`client_id=${config.get('apis.github.client')}`,					`redirect_uri=${config.get('serverDomain')}/auth/github/authorize/callback`,					`scope=user:email`				].join('&');				res.redirect(`https://github.com/login/oauth/authorize?${params}`);			});			app.get('/auth/github/link', async (req, res) => {				try { await this._validateHook(); } catch { return; }				let params = [					`client_id=${config.get('apis.github.client')}`,					`redirect_uri=${config.get('serverDomain')}/auth/github/authorize/callback`,					`scope=user:email`,					`state=${req.cookies[SIDname]}`				].join('&');				res.redirect(`https://github.com/login/oauth/authorize?${params}`);			});			function redirectOnErr (res, err){				return res.redirect(`${config.get('domain')}/?err=${encodeURIComponent(err)}`);			}			app.get('/auth/github/authorize/callback', async (req, res) => {				try { await this._validateHook(); } catch { return; }				let code = req.query.code;				let access_token;				let body;				let address;				const state = req.query.state;				async.waterfall([					(next) => {						if (req.query.error) return next(req.query.error_description);						next();					},					(next) => {						oauth2.getOAuthAccessToken(code, {redirect_uri}, next);					},					(_access_token, refresh_token, results, next) => {						if (results.error) return next(results.error_description);						access_token = _access_token;						request.get({							url: `https://api.github.com/user?access_token=${access_token}`,							headers: {'User-Agent': 'request'}						}, next);					},					(httpResponse, _body, next) => {						body = _body = JSON.parse(_body);						if (httpResponse.statusCode !== 200) return next(body.message);						if (state) {							return async.waterfall([								(next) => {									cache.hget('sessions', state, next);								},								(session, next) => {									if (!session) return next('Invalid session.');									db.models.user.findOne({_id: session.userId}, next);								},								(user, next) => {									if (!user) return next('User not found.');									if (user.services.github && user.services.github.id) return next('Account already has GitHub linked.');									db.models.user.updateOne({_id: user._id}, {$set: {"services.github": {id: body.id, access_token}}}, {runValidators: true}, (err) => {										if (err) return next(err);										next(null, user, body);									});								},								(user) => {									cache.pub('user.linkGitHub', user._id);									res.redirect(`${config.get('domain')}/settings`);								}							], next);						}						if (!body.id) return next("Something went wrong, no id.");						db.models.user.findOne({'services.github.id': body.id}, (err, user) => {							next(err, user, body);						});					},					(user, body, next) => {						if (user) {							user.services.github.access_token = access_token;							return user.save(() => {								next(true, user._id);							});						}						db.models.user.findOne({ username: new RegExp(`^${body.login}$`, 'i' )}, (err, user) => {							next(err, user);						});					},					(user, next) => {						if (user) return next('An account with that username already exists.');						request.get({							url: `https://api.github.com/user/emails?access_token=${access_token}`,							headers: {'User-Agent': 'request'}						}, next);					},					(httpResponse, body2, next) => {						body2 = JSON.parse(body2);						if (!Array.isArray(body2)) return next(body2.message);						body2.forEach(email => {							if (email.primary) address = email.email.toLowerCase();						});						db.models.user.findOne({'email.address': address}, next);					},					async (user, next) => {						const verificationToken = await this.utils.generateRandomString(64);						if (user) return next('An account with that email address already exists.');						db.models.user.create({							_id: await this.utils.generateRandomString(12),//TODO Check if exists							username: body.login,							email: {								address,								verificationToken: verificationToken							},							services: {								github: {id: body.id, access_token}							}						}, next);					},					(user, next) => {						mail.schemas.verifyEmail(address, body.login, user.email.verificationToken);						next(null, user._id);					}				], async (err, userId) => {					if (err && err !== true) {						err = await this.utils.getError(err);						logger.error('AUTH_GITHUB_AUTHORIZE_CALLBACK', `Failed to authorize with GitHub. "${err}"`);						return redirectOnErr(res, err);					}					const sessionId = await this.utils.guid();					cache.hset('sessions', sessionId, cache.schemas.session(sessionId, userId), err => {						if (err) return redirectOnErr(res, err.message);						let date = new Date();						date.setTime(new Date().getTime() + (2 * 365 * 24 * 60 * 60 * 1000));						res.cookie(SIDname, sessionId, {							expires: date,							secure: config.get("cookie.secure"),							path: "/",							domain: config.get("cookie.domain")						});						logger.success('AUTH_GITHUB_AUTHORIZE_CALLBACK', `User "${userId}" successfully authorized with GitHub.`);						res.redirect(`${config.get('domain')}/`);					});				});			});			app.get('/auth/verify_email', async (req, res) => {				try { await this._validateHook(); } catch { return; }				let code = req.query.code;				async.waterfall([					(next) => {						if (!code) return next('Invalid code.');						next();					},					(next) => {						db.models.user.findOne({"email.verificationToken": code}, next);					},					(user, next) => {						if (!user) return next('User not found.');						if (user.email.verified) return next('This email is already verified.');						db.models.user.updateOne({"email.verificationToken": code}, {$set: {"email.verified": true}, $unset: {"email.verificationToken": ''}}, {runValidators: true}, next);					}				], (err) => {					if (err) {						let error = 'An error occurred.';						if (typeof err === "string") error = err;						else if (err.message) error = err.message;						logger.error("VERIFY_EMAIL", `Verifying email failed. "${error}"`);						return res.json({ status: 'failure', message: error});					}					logger.success("VERIFY_EMAIL", `Successfully verified email.`);					res.redirect(`${config.get("domain")}?msg=Thank you for verifying your email`);				});			});			resolve();		});	}}
 |