user authentication thingy
This commit is contained in:
parent
9fb3557bab
commit
0885bf9c6a
14 changed files with 615 additions and 13 deletions
|
@ -14,10 +14,12 @@
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/express": "^4.17.13",
|
"@types/express": "^4.17.13",
|
||||||
|
"@types/monk": "^6.0.0",
|
||||||
"@types/ws": "^8.2.2",
|
"@types/ws": "^8.2.2",
|
||||||
"dotenv": "^14.2.0",
|
"dotenv": "^14.2.0",
|
||||||
"express": "^4.17.2",
|
"express": "^4.17.2",
|
||||||
"log75": "^2.2.0",
|
"log75": "^2.2.0",
|
||||||
|
"monk": "^7.3.4",
|
||||||
"ws": "^8.4.2"
|
"ws": "^8.4.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
29
api/src/db.ts
Normal file
29
api/src/db.ts
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
import Monk, { IMonkManager } from 'monk';
|
||||||
|
import { logger } from '.';
|
||||||
|
|
||||||
|
export default (): IMonkManager => {
|
||||||
|
let dburl = getDBUrl();
|
||||||
|
let db = Monk(dburl);
|
||||||
|
return db;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Checks if all required env vars were supplied, and returns the mongo db URL
|
||||||
|
function getDBUrl() {
|
||||||
|
let env = process.env;
|
||||||
|
if (env['DB_URL']) return env['DB_URL'];
|
||||||
|
|
||||||
|
if (!env['DB_HOST']) {
|
||||||
|
logger.error(`Environment variable 'DB_HOST' not set, unable to connect to database`);
|
||||||
|
logger.error(`Specify either 'DB_URL' or 'DB_HOST', 'DB_USERNAME', 'DB_PASS' and 'DB_NAME'`);
|
||||||
|
throw 'Missing environment variables';
|
||||||
|
}
|
||||||
|
|
||||||
|
// mongodb://username:password@hostname:port/dbname
|
||||||
|
let dburl = 'mongodb://';
|
||||||
|
if (env['DB_USERNAME']) dburl += env['DB_USERNAME'];
|
||||||
|
if (env['DB_PASS']) dburl += `:${env['DB_PASS']}`;
|
||||||
|
dburl += `${process.env['DB_USERNAME'] ? '@' : ''}${env['DB_HOST']}`; // DB_HOST is assumed to contain the port
|
||||||
|
dburl += `/${env['DB_NAME'] ?? 'automod'}`;
|
||||||
|
|
||||||
|
return dburl;
|
||||||
|
}
|
|
@ -1,22 +1,29 @@
|
||||||
import { config } from 'dotenv';
|
import { config } from 'dotenv';
|
||||||
import Express from "express";
|
import Express from "express";
|
||||||
import Log75, { LogLevel } from 'log75';
|
import Log75, { LogLevel } from 'log75';
|
||||||
|
import buildDBClient from './db';
|
||||||
|
|
||||||
config();
|
config();
|
||||||
|
|
||||||
const PORT = Number(process.env.API_PORT || 9000);
|
const PORT = Number(process.env.API_PORT || 9000);
|
||||||
const DEBUG = process.env.NODE_ENV != 'production';
|
const DEBUG = process.env.NODE_ENV != 'production';
|
||||||
|
const SESSION_LIFETIME = 1000 * 60 * 60 * 24 * 7;
|
||||||
|
|
||||||
const logger: Log75 = new (Log75 as any).default(DEBUG ? LogLevel.Debug : LogLevel.Standard);
|
const logger: Log75 = new (Log75 as any).default(DEBUG ? LogLevel.Debug : LogLevel.Standard);
|
||||||
|
const db = buildDBClient();
|
||||||
const app = Express();
|
const app = Express();
|
||||||
|
|
||||||
export { logger, app, PORT }
|
app.use(Express.json());
|
||||||
|
|
||||||
|
export { logger, app, db, PORT, SESSION_LIFETIME }
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
import('./middlewares/log'),
|
import('./middlewares/log'),
|
||||||
|
import('./middlewares/updateTokenExpiry'),
|
||||||
import('./routes/internal/ws'),
|
import('./routes/internal/ws'),
|
||||||
import('./routes/root'),
|
import('./routes/root'),
|
||||||
|
import('./routes/login'),
|
||||||
]);
|
]);
|
||||||
logger.done('All routes and middlewares loaded');
|
logger.done('All routes and middlewares loaded');
|
||||||
})();
|
})();
|
||||||
|
|
|
@ -2,6 +2,6 @@ import { Request, Response } from "express";
|
||||||
import { app, logger } from "..";
|
import { app, logger } from "..";
|
||||||
|
|
||||||
app.use('*', (req: Request, res: Response, next: () => void) => {
|
app.use('*', (req: Request, res: Response, next: () => void) => {
|
||||||
logger.debug(`${req.method} ${req.path}`);
|
logger.debug(`${req.method} ${req.url}`);
|
||||||
next();
|
next();
|
||||||
});
|
});
|
||||||
|
|
19
api/src/middlewares/updateTokenExpiry.ts
Normal file
19
api/src/middlewares/updateTokenExpiry.ts
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
import { Request, Response } from "express";
|
||||||
|
import { FindOneResult } from "monk";
|
||||||
|
import { app, db, SESSION_LIFETIME } from "..";
|
||||||
|
|
||||||
|
app.use('*', async (req: Request, res: Response, next: () => void) => {
|
||||||
|
next();
|
||||||
|
|
||||||
|
const user = req.header('x-auth-user');
|
||||||
|
const token = req.header('x-auth-token');
|
||||||
|
|
||||||
|
if (!user || !token) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const session: FindOneResult<any> = await db.get('sessions').findOne({ user, token, expires: { $gt: Date.now() } });
|
||||||
|
if (session) {
|
||||||
|
await db.get('sessions').update({ _id: session._id }, { $set: { expires: Date.now() + SESSION_LIFETIME } });
|
||||||
|
}
|
||||||
|
} catch(e) { console.error(e) }
|
||||||
|
});
|
|
@ -29,8 +29,12 @@ wsServer.on('connection', (sock) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
sock.on('message', (msg) => {
|
sock.on('message', (msg) => {
|
||||||
|
const jsonBody = JSON.parse(msg.toString());
|
||||||
logger.debug(`[WS] [<] ${msg.toString()}`);
|
logger.debug(`[WS] [<] ${msg.toString()}`);
|
||||||
botWS.emit('message', JSON.parse(msg.toString()));
|
botWS.emit('message', jsonBody);
|
||||||
|
if (jsonBody.data && jsonBody.type) {
|
||||||
|
botWS.emit(jsonBody.type, jsonBody.data);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -61,8 +65,23 @@ function sendBotWS(msg: { [key: string]: any }) {
|
||||||
socks.forEach(sock => sock.send(JSON.stringify(msg)));
|
socks.forEach(sock => sock.send(JSON.stringify(msg)));
|
||||||
}
|
}
|
||||||
|
|
||||||
botWS.on('message', msg => {
|
type botReqRes = { success: false, error: string, statusCode?: number } | { success: true, [key: string]: any }
|
||||||
sendBotWS({ amogus: msg });
|
function botReq(type: string, data?: { [key: string]: any }): Promise<botReqRes> {
|
||||||
});
|
return new Promise((resolve, reject) => {
|
||||||
|
const nonce = `${Date.now()}.${Math.round(Math.random() * 10000000)}`;
|
||||||
|
if (sockets.length == 0) return resolve({ success: false, error: 'Unable to communicate with bot' });
|
||||||
|
sendBotWS({ nonce, type, data });
|
||||||
|
botWS.once(`response:${nonce}`, (data: string|Object) => {
|
||||||
|
try {
|
||||||
|
const d = typeof data == 'string' ? JSON.parse(data || '{}') : data;
|
||||||
|
if (d.success == undefined) d.success = true;
|
||||||
|
if (d.success == false && !d.error) d.error = 'Unknown error';
|
||||||
|
resolve(d);
|
||||||
|
} catch(e) { reject(e) }
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export { botWS, sendBotWS }
|
//setInterval(() => botReq('test', { "sus": true }), 1000);
|
||||||
|
|
||||||
|
export { botWS, sendBotWS, botReq }
|
||||||
|
|
67
api/src/routes/login.ts
Normal file
67
api/src/routes/login.ts
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
import crypto from 'crypto';
|
||||||
|
import { app, SESSION_LIFETIME } from '..';
|
||||||
|
import { Request, Response } from 'express';
|
||||||
|
import { botReq } from './internal/ws';
|
||||||
|
import { db } from '..';
|
||||||
|
import { FindOneResult } from 'monk';
|
||||||
|
|
||||||
|
class BeginReqBody {
|
||||||
|
user: string;
|
||||||
|
}
|
||||||
|
class CompleteReqBody {
|
||||||
|
user: string;
|
||||||
|
nonce: string;
|
||||||
|
code: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
app.post('/login/begin', async (req: Request, res: Response) => {
|
||||||
|
const body = req.body as BeginReqBody;
|
||||||
|
if (!body.user || typeof body.user != 'string') return badRequest(res);
|
||||||
|
|
||||||
|
const r = await botReq('requestLogin', { user: body.user.toLowerCase() });
|
||||||
|
|
||||||
|
if (!r.success) return res.status(r.statusCode ?? 500).send(JSON.stringify({ error: r.error }, null, 4));
|
||||||
|
|
||||||
|
res.status(200).send({ success: true, nonce: r.nonce, code: r.code });
|
||||||
|
});
|
||||||
|
|
||||||
|
app.post('/login/complete', async (req: Request, res: Response) => {
|
||||||
|
const body = req.body as CompleteReqBody;
|
||||||
|
if ((!body.user || typeof body.user != 'string') ||
|
||||||
|
(!body.nonce || typeof body.nonce != 'string') ||
|
||||||
|
(!body.code || typeof body.code != 'string')) return badRequest(res);
|
||||||
|
|
||||||
|
const loginAttempt: FindOneResult<any> = await db.get('pending_logins').findOne({
|
||||||
|
code: body.code,
|
||||||
|
user: body.user,
|
||||||
|
nonce: body.nonce,
|
||||||
|
exchanged: false,
|
||||||
|
invalid: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!loginAttempt) return res.status(404).send({ error: 'The provided login info could not be found.' });
|
||||||
|
|
||||||
|
if (!loginAttempt.confirmed) {
|
||||||
|
return res.status(400).send({ error: "This code is not yet valid." });
|
||||||
|
}
|
||||||
|
|
||||||
|
const sessionToken = crypto.randomBytes(48).toString('base64').replace(/=/g, '');
|
||||||
|
|
||||||
|
|
||||||
|
await Promise.all([
|
||||||
|
db.get('sessions').insert({
|
||||||
|
user: body.user.toUpperCase(),
|
||||||
|
token: sessionToken,
|
||||||
|
nonce: body.nonce,
|
||||||
|
invalid: false,
|
||||||
|
expires: Date.now() + SESSION_LIFETIME,
|
||||||
|
}),
|
||||||
|
db.get('pending_logins').update({ _id: loginAttempt._id }, { $set: { exchanged: true } }),
|
||||||
|
]);
|
||||||
|
|
||||||
|
res.status(200).send({ success: true, user: body.user.toUpperCase(), token: sessionToken });
|
||||||
|
});
|
||||||
|
|
||||||
|
function badRequest(res: Response) {
|
||||||
|
res.status(400).send(JSON.stringify({ "error": "Invalid request body" }, null, 4));
|
||||||
|
}
|
|
@ -1,6 +1,11 @@
|
||||||
import { app } from '..';
|
import { app } from '..';
|
||||||
import { Request, Response } from 'express';
|
import { Request, Response } from 'express';
|
||||||
|
import { getSessionInfo, isAuthenticated } from '../utils';
|
||||||
|
|
||||||
app.get('/', (req: Request, res: Response) => {
|
app.get('/', async (req: Request, res: Response) => {
|
||||||
res.send({ msg: "yo" });
|
const isAuthed = await isAuthenticated(req);
|
||||||
|
res.send({
|
||||||
|
authenticated: isAuthed,
|
||||||
|
sessionInfo: isAuthed ? await getSessionInfo(req.header('x-auth-user')!, req.header('x-auth-token')!) : {},
|
||||||
|
});
|
||||||
});
|
});
|
36
api/src/utils.ts
Normal file
36
api/src/utils.ts
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
import { Request } from "express";
|
||||||
|
import { FindOneResult } from "monk";
|
||||||
|
import { db } from ".";
|
||||||
|
|
||||||
|
class Session {
|
||||||
|
user: string;
|
||||||
|
token: string;
|
||||||
|
nonce: string;
|
||||||
|
expires: number;
|
||||||
|
invalid: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param req
|
||||||
|
* @returns false if not authenticated, otherwise the (Revolt) user ID
|
||||||
|
*/
|
||||||
|
async function isAuthenticated(req: Request): Promise<string|false> {
|
||||||
|
const user = req.header('x-auth-user');
|
||||||
|
const token = req.header('x-auth-token');
|
||||||
|
|
||||||
|
if (!user || !token) return false;
|
||||||
|
|
||||||
|
const info = await getSessionInfo(user, token);
|
||||||
|
return info.valid ? user : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
type SessionInfo = { exists: boolean, valid: boolean, nonce?: string }
|
||||||
|
|
||||||
|
async function getSessionInfo(user: string, token: string): Promise<SessionInfo> {
|
||||||
|
const session: FindOneResult<Session> = await db.get('sessions').findOne({ user, token });
|
||||||
|
|
||||||
|
return { exists: !!session, valid: !!(session && !session.invalid && session.expires > Date.now()), nonce: session?.nonce }
|
||||||
|
}
|
||||||
|
|
||||||
|
export { isAuthenticated, getSessionInfo }
|
221
api/yarn.lock
221
api/yarn.lock
|
@ -10,6 +10,13 @@
|
||||||
"@types/connect" "*"
|
"@types/connect" "*"
|
||||||
"@types/node" "*"
|
"@types/node" "*"
|
||||||
|
|
||||||
|
"@types/bson@*":
|
||||||
|
version "4.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/bson/-/bson-4.2.0.tgz#a2f71e933ff54b2c3bf267b67fa221e295a33337"
|
||||||
|
integrity sha512-ELCPqAdroMdcuxqwMgUpifQyRoTpyYCNr1V9xKyF40VsBobsj+BbWNRvwGchMgBPGqkw655ypkjj2MEF5ywVwg==
|
||||||
|
dependencies:
|
||||||
|
bson "*"
|
||||||
|
|
||||||
"@types/connect@*":
|
"@types/connect@*":
|
||||||
version "3.4.35"
|
version "3.4.35"
|
||||||
resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.35.tgz#5fcf6ae445e4021d1fc2219a4873cc73a3bb2ad1"
|
resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.35.tgz#5fcf6ae445e4021d1fc2219a4873cc73a3bb2ad1"
|
||||||
|
@ -41,6 +48,21 @@
|
||||||
resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.2.tgz#93e25bf9ee75fe0fd80b594bc4feb0e862111b5a"
|
resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.2.tgz#93e25bf9ee75fe0fd80b594bc4feb0e862111b5a"
|
||||||
integrity sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==
|
integrity sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==
|
||||||
|
|
||||||
|
"@types/mongodb@^3.5.25":
|
||||||
|
version "3.6.20"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/mongodb/-/mongodb-3.6.20.tgz#b7c5c580644f6364002b649af1c06c3c0454e1d2"
|
||||||
|
integrity sha512-WcdpPJCakFzcWWD9juKoZbRtQxKIMYF/JIAM4JrNHrMcnJL6/a2NWjXxW7fo9hxboxxkg+icff8d7+WIEvKgYQ==
|
||||||
|
dependencies:
|
||||||
|
"@types/bson" "*"
|
||||||
|
"@types/node" "*"
|
||||||
|
|
||||||
|
"@types/monk@^6.0.0":
|
||||||
|
version "6.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/monk/-/monk-6.0.0.tgz#236750988e24d92c674529a81b9a296f8bbc3763"
|
||||||
|
integrity sha512-9qy4Gva0uVgaQsDqlcWD+XOXUmgInPAoxyyoN8uFUTjNFvswyCH1hwpnYuh2MVr60ekZgYiquWEjBYvfYfE1Jw==
|
||||||
|
dependencies:
|
||||||
|
monk "*"
|
||||||
|
|
||||||
"@types/node@*":
|
"@types/node@*":
|
||||||
version "17.0.10"
|
version "17.0.10"
|
||||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.10.tgz#616f16e9d3a2a3d618136b1be244315d95bd7cab"
|
resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.10.tgz#616f16e9d3a2a3d618136b1be244315d95bd7cab"
|
||||||
|
@ -89,6 +111,19 @@ array-flatten@1.1.1:
|
||||||
resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2"
|
resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2"
|
||||||
integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=
|
integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=
|
||||||
|
|
||||||
|
base64-js@^1.3.1:
|
||||||
|
version "1.5.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
|
||||||
|
integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
|
||||||
|
|
||||||
|
bl@^2.2.1:
|
||||||
|
version "2.2.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/bl/-/bl-2.2.1.tgz#8c11a7b730655c5d56898cdc871224f40fd901d5"
|
||||||
|
integrity sha512-6Pesp1w0DEX1N550i/uGV/TqucVL4AM/pgThFSN/Qq9si1/DF9aIHs1BxD8V/QU0HoeHO6cQRTAuYnLPKq1e4g==
|
||||||
|
dependencies:
|
||||||
|
readable-stream "^2.3.5"
|
||||||
|
safe-buffer "^5.1.1"
|
||||||
|
|
||||||
body-parser@1.19.1:
|
body-parser@1.19.1:
|
||||||
version "1.19.1"
|
version "1.19.1"
|
||||||
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.1.tgz#1499abbaa9274af3ecc9f6f10396c995943e31d4"
|
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.1.tgz#1499abbaa9274af3ecc9f6f10396c995943e31d4"
|
||||||
|
@ -105,6 +140,26 @@ body-parser@1.19.1:
|
||||||
raw-body "2.4.2"
|
raw-body "2.4.2"
|
||||||
type-is "~1.6.18"
|
type-is "~1.6.18"
|
||||||
|
|
||||||
|
bson@*:
|
||||||
|
version "4.6.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/bson/-/bson-4.6.1.tgz#2b5da517539bb0f7f3ffb54ac70a384ca899641c"
|
||||||
|
integrity sha512-I1LQ7Hz5zgwR4QquilLNZwbhPw0Apx7i7X9kGMBTsqPdml/03Q9NBtD9nt/19ahjlphktQImrnderxqpzeVDjw==
|
||||||
|
dependencies:
|
||||||
|
buffer "^5.6.0"
|
||||||
|
|
||||||
|
bson@^1.1.4:
|
||||||
|
version "1.1.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/bson/-/bson-1.1.6.tgz#fb819be9a60cd677e0853aee4ca712a785d6618a"
|
||||||
|
integrity sha512-EvVNVeGo4tHxwi8L6bPj3y3itEvStdwvvlojVxxbyYfoaxJ6keLgrTuKdyfEAszFK+H3olzBuafE0yoh0D1gdg==
|
||||||
|
|
||||||
|
buffer@^5.6.0:
|
||||||
|
version "5.7.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0"
|
||||||
|
integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==
|
||||||
|
dependencies:
|
||||||
|
base64-js "^1.3.1"
|
||||||
|
ieee754 "^1.1.13"
|
||||||
|
|
||||||
bytes@3.1.1:
|
bytes@3.1.1:
|
||||||
version "3.1.1"
|
version "3.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.1.tgz#3f018291cb4cbad9accb6e6970bca9c8889e879a"
|
resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.1.tgz#3f018291cb4cbad9accb6e6970bca9c8889e879a"
|
||||||
|
@ -132,6 +187,18 @@ cookie@0.4.1:
|
||||||
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.1.tgz#afd713fe26ebd21ba95ceb61f9a8116e50a537d1"
|
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.1.tgz#afd713fe26ebd21ba95ceb61f9a8116e50a537d1"
|
||||||
integrity sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==
|
integrity sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==
|
||||||
|
|
||||||
|
core-util-is@~1.0.0:
|
||||||
|
version "1.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85"
|
||||||
|
integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==
|
||||||
|
|
||||||
|
debug@*:
|
||||||
|
version "4.3.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664"
|
||||||
|
integrity sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==
|
||||||
|
dependencies:
|
||||||
|
ms "2.1.2"
|
||||||
|
|
||||||
debug@2.6.9:
|
debug@2.6.9:
|
||||||
version "2.6.9"
|
version "2.6.9"
|
||||||
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
|
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
|
||||||
|
@ -139,6 +206,11 @@ debug@2.6.9:
|
||||||
dependencies:
|
dependencies:
|
||||||
ms "2.0.0"
|
ms "2.0.0"
|
||||||
|
|
||||||
|
denque@^1.4.1:
|
||||||
|
version "1.5.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/denque/-/denque-1.5.1.tgz#07f670e29c9a78f8faecb2566a1e2c11929c5cbf"
|
||||||
|
integrity sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw==
|
||||||
|
|
||||||
depd@~1.1.2:
|
depd@~1.1.2:
|
||||||
version "1.1.2"
|
version "1.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9"
|
resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9"
|
||||||
|
@ -251,7 +323,12 @@ iconv-lite@0.4.24:
|
||||||
dependencies:
|
dependencies:
|
||||||
safer-buffer ">= 2.1.2 < 3"
|
safer-buffer ">= 2.1.2 < 3"
|
||||||
|
|
||||||
inherits@2.0.4:
|
ieee754@^1.1.13:
|
||||||
|
version "1.2.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
|
||||||
|
integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==
|
||||||
|
|
||||||
|
inherits@2.0.4, inherits@~2.0.3:
|
||||||
version "2.0.4"
|
version "2.0.4"
|
||||||
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
|
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
|
||||||
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
|
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
|
||||||
|
@ -261,6 +338,11 @@ ipaddr.js@1.9.1:
|
||||||
resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3"
|
resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3"
|
||||||
integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==
|
integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==
|
||||||
|
|
||||||
|
isarray@~1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
|
||||||
|
integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=
|
||||||
|
|
||||||
log75@^2.2.0:
|
log75@^2.2.0:
|
||||||
version "2.2.0"
|
version "2.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/log75/-/log75-2.2.0.tgz#2505159b450c3cd1b4e863a837e883a7ecbaa2a1"
|
resolved "https://registry.yarnpkg.com/log75/-/log75-2.2.0.tgz#2505159b450c3cd1b4e863a837e883a7ecbaa2a1"
|
||||||
|
@ -273,6 +355,11 @@ media-typer@0.3.0:
|
||||||
resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
|
resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
|
||||||
integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=
|
integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=
|
||||||
|
|
||||||
|
memory-pager@^1.0.2:
|
||||||
|
version "1.5.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/memory-pager/-/memory-pager-1.5.0.tgz#d8751655d22d384682741c972f2c3d6dfa3e66b5"
|
||||||
|
integrity sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==
|
||||||
|
|
||||||
merge-descriptors@1.0.1:
|
merge-descriptors@1.0.1:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61"
|
resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61"
|
||||||
|
@ -300,11 +387,75 @@ mime@1.6.0:
|
||||||
resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
|
resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
|
||||||
integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==
|
integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==
|
||||||
|
|
||||||
|
mongodb@^3.2.3:
|
||||||
|
version "3.7.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/mongodb/-/mongodb-3.7.3.tgz#b7949cfd0adc4cc7d32d3f2034214d4475f175a5"
|
||||||
|
integrity sha512-Psm+g3/wHXhjBEktkxXsFMZvd3nemI0r3IPsE0bU+4//PnvNWKkzhZcEsbPcYiWqe8XqXJJEg4Tgtr7Raw67Yw==
|
||||||
|
dependencies:
|
||||||
|
bl "^2.2.1"
|
||||||
|
bson "^1.1.4"
|
||||||
|
denque "^1.4.1"
|
||||||
|
optional-require "^1.1.8"
|
||||||
|
safe-buffer "^5.1.2"
|
||||||
|
optionalDependencies:
|
||||||
|
saslprep "^1.0.0"
|
||||||
|
|
||||||
|
monk-middleware-cast-ids@^0.2.1:
|
||||||
|
version "0.2.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/monk-middleware-cast-ids/-/monk-middleware-cast-ids-0.2.1.tgz#40c40e5a6cb33ccedc289220943275ee8861c529"
|
||||||
|
integrity sha1-QMQOWmyzPM7cKJIglDJ17ohhxSk=
|
||||||
|
|
||||||
|
monk-middleware-fields@^0.2.0:
|
||||||
|
version "0.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/monk-middleware-fields/-/monk-middleware-fields-0.2.0.tgz#ff637af35f5948879ccb2be15a91360911bea6c1"
|
||||||
|
integrity sha1-/2N6819ZSIecyyvhWpE2CRG+psE=
|
||||||
|
|
||||||
|
monk-middleware-handle-callback@^0.2.0:
|
||||||
|
version "0.2.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/monk-middleware-handle-callback/-/monk-middleware-handle-callback-0.2.2.tgz#47de6cc1248726c72a2be0c81bc4e68310c32146"
|
||||||
|
integrity sha512-5hBynb7asZ2uw9XVze7C3XH0zXT51yFDvYydk/5HnWWzh2NLglDSiKDcX0yLKPHzFgiq+5Z4Laq5fFVnFsmm8w==
|
||||||
|
|
||||||
|
monk-middleware-options@^0.2.1:
|
||||||
|
version "0.2.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/monk-middleware-options/-/monk-middleware-options-0.2.1.tgz#58dae1c518d46636ebdff506fadfc773bb442886"
|
||||||
|
integrity sha1-WNrhxRjUZjbr3/UG+t/Hc7tEKIY=
|
||||||
|
|
||||||
|
monk-middleware-query@^0.2.0:
|
||||||
|
version "0.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/monk-middleware-query/-/monk-middleware-query-0.2.0.tgz#a926c677d4a5620c62151b0a56d0c0c151675874"
|
||||||
|
integrity sha1-qSbGd9SlYgxiFRsKVtDAwVFnWHQ=
|
||||||
|
|
||||||
|
monk-middleware-wait-for-connection@^0.2.0:
|
||||||
|
version "0.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/monk-middleware-wait-for-connection/-/monk-middleware-wait-for-connection-0.2.0.tgz#312958d30e588b57d09754dd7c97b4843316835a"
|
||||||
|
integrity sha1-MSlY0w5Yi1fQl1TdfJe0hDMWg1o=
|
||||||
|
|
||||||
|
monk@*, monk@^7.3.4:
|
||||||
|
version "7.3.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/monk/-/monk-7.3.4.tgz#50ccd7daebb4c16ace58d45b2c28c29112f8a85e"
|
||||||
|
integrity sha512-PkPNiElwroVyKQj01usyziOvwiKYBUVSq7YU1FB4KFr0J3v0GeXW0TebYsLR4u33WB8JGqPiAcuzDspfdujqQg==
|
||||||
|
dependencies:
|
||||||
|
"@types/mongodb" "^3.5.25"
|
||||||
|
debug "*"
|
||||||
|
mongodb "^3.2.3"
|
||||||
|
monk-middleware-cast-ids "^0.2.1"
|
||||||
|
monk-middleware-fields "^0.2.0"
|
||||||
|
monk-middleware-handle-callback "^0.2.0"
|
||||||
|
monk-middleware-options "^0.2.1"
|
||||||
|
monk-middleware-query "^0.2.0"
|
||||||
|
monk-middleware-wait-for-connection "^0.2.0"
|
||||||
|
object-assign "^4.1.1"
|
||||||
|
|
||||||
ms@2.0.0:
|
ms@2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
|
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
|
||||||
integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=
|
integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=
|
||||||
|
|
||||||
|
ms@2.1.2:
|
||||||
|
version "2.1.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
|
||||||
|
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
|
||||||
|
|
||||||
ms@2.1.3:
|
ms@2.1.3:
|
||||||
version "2.1.3"
|
version "2.1.3"
|
||||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
|
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
|
||||||
|
@ -315,6 +466,11 @@ negotiator@0.6.2:
|
||||||
resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb"
|
resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb"
|
||||||
integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==
|
integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==
|
||||||
|
|
||||||
|
object-assign@^4.1.1:
|
||||||
|
version "4.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
|
||||||
|
integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
|
||||||
|
|
||||||
on-finished@~2.3.0:
|
on-finished@~2.3.0:
|
||||||
version "2.3.0"
|
version "2.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947"
|
resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947"
|
||||||
|
@ -322,6 +478,13 @@ on-finished@~2.3.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
ee-first "1.1.1"
|
ee-first "1.1.1"
|
||||||
|
|
||||||
|
optional-require@^1.1.8:
|
||||||
|
version "1.1.8"
|
||||||
|
resolved "https://registry.yarnpkg.com/optional-require/-/optional-require-1.1.8.tgz#16364d76261b75d964c482b2406cb824d8ec44b7"
|
||||||
|
integrity sha512-jq83qaUb0wNg9Krv1c5OQ+58EK+vHde6aBPzLvPPqJm89UQWsvSuFy9X/OSNJnFeSOKo7btE0n8Nl2+nE+z5nA==
|
||||||
|
dependencies:
|
||||||
|
require-at "^1.0.6"
|
||||||
|
|
||||||
parseurl@~1.3.3:
|
parseurl@~1.3.3:
|
||||||
version "1.3.3"
|
version "1.3.3"
|
||||||
resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4"
|
resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4"
|
||||||
|
@ -332,6 +495,11 @@ path-to-regexp@0.1.7:
|
||||||
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c"
|
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c"
|
||||||
integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=
|
integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=
|
||||||
|
|
||||||
|
process-nextick-args@~2.0.0:
|
||||||
|
version "2.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
|
||||||
|
integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==
|
||||||
|
|
||||||
proxy-addr@~2.0.7:
|
proxy-addr@~2.0.7:
|
||||||
version "2.0.7"
|
version "2.0.7"
|
||||||
resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025"
|
resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025"
|
||||||
|
@ -360,16 +528,46 @@ raw-body@2.4.2:
|
||||||
iconv-lite "0.4.24"
|
iconv-lite "0.4.24"
|
||||||
unpipe "1.0.0"
|
unpipe "1.0.0"
|
||||||
|
|
||||||
safe-buffer@5.2.1:
|
readable-stream@^2.3.5:
|
||||||
|
version "2.3.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57"
|
||||||
|
integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==
|
||||||
|
dependencies:
|
||||||
|
core-util-is "~1.0.0"
|
||||||
|
inherits "~2.0.3"
|
||||||
|
isarray "~1.0.0"
|
||||||
|
process-nextick-args "~2.0.0"
|
||||||
|
safe-buffer "~5.1.1"
|
||||||
|
string_decoder "~1.1.1"
|
||||||
|
util-deprecate "~1.0.1"
|
||||||
|
|
||||||
|
require-at@^1.0.6:
|
||||||
|
version "1.0.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/require-at/-/require-at-1.0.6.tgz#9eb7e3c5e00727f5a4744070a7f560d4de4f6e6a"
|
||||||
|
integrity sha512-7i1auJbMUrXEAZCOQ0VNJgmcT2VOKPRl2YGJwgpHpC9CE91Mv4/4UYIUm4chGJaI381ZDq1JUicFii64Hapd8g==
|
||||||
|
|
||||||
|
safe-buffer@5.2.1, safe-buffer@^5.1.1, safe-buffer@^5.1.2:
|
||||||
version "5.2.1"
|
version "5.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
|
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
|
||||||
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
|
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
|
||||||
|
|
||||||
|
safe-buffer@~5.1.0, safe-buffer@~5.1.1:
|
||||||
|
version "5.1.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
|
||||||
|
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
|
||||||
|
|
||||||
"safer-buffer@>= 2.1.2 < 3":
|
"safer-buffer@>= 2.1.2 < 3":
|
||||||
version "2.1.2"
|
version "2.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
|
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
|
||||||
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
|
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
|
||||||
|
|
||||||
|
saslprep@^1.0.0:
|
||||||
|
version "1.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/saslprep/-/saslprep-1.0.3.tgz#4c02f946b56cf54297e347ba1093e7acac4cf226"
|
||||||
|
integrity sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag==
|
||||||
|
dependencies:
|
||||||
|
sparse-bitfield "^3.0.3"
|
||||||
|
|
||||||
send@0.17.2:
|
send@0.17.2:
|
||||||
version "0.17.2"
|
version "0.17.2"
|
||||||
resolved "https://registry.yarnpkg.com/send/-/send-0.17.2.tgz#926622f76601c41808012c8bf1688fe3906f7820"
|
resolved "https://registry.yarnpkg.com/send/-/send-0.17.2.tgz#926622f76601c41808012c8bf1688fe3906f7820"
|
||||||
|
@ -404,11 +602,25 @@ setprototypeof@1.2.0:
|
||||||
resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424"
|
resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424"
|
||||||
integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==
|
integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==
|
||||||
|
|
||||||
|
sparse-bitfield@^3.0.3:
|
||||||
|
version "3.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz#ff4ae6e68656056ba4b3e792ab3334d38273ca11"
|
||||||
|
integrity sha1-/0rm5oZWBWuks+eSqzM004JzyhE=
|
||||||
|
dependencies:
|
||||||
|
memory-pager "^1.0.2"
|
||||||
|
|
||||||
"statuses@>= 1.5.0 < 2", statuses@~1.5.0:
|
"statuses@>= 1.5.0 < 2", statuses@~1.5.0:
|
||||||
version "1.5.0"
|
version "1.5.0"
|
||||||
resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c"
|
resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c"
|
||||||
integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=
|
integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=
|
||||||
|
|
||||||
|
string_decoder@~1.1.1:
|
||||||
|
version "1.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8"
|
||||||
|
integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==
|
||||||
|
dependencies:
|
||||||
|
safe-buffer "~5.1.0"
|
||||||
|
|
||||||
toidentifier@1.0.1:
|
toidentifier@1.0.1:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35"
|
resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35"
|
||||||
|
@ -427,6 +639,11 @@ unpipe@1.0.0, unpipe@~1.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
|
resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
|
||||||
integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=
|
integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=
|
||||||
|
|
||||||
|
util-deprecate@~1.0.1:
|
||||||
|
version "1.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
|
||||||
|
integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
|
||||||
|
|
||||||
utils-merge@1.0.1:
|
utils-merge@1.0.1:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
|
resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
|
||||||
|
|
62
bot/src/bot/commands/login.ts
Normal file
62
bot/src/bot/commands/login.ts
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
import { FindOneResult } from "monk";
|
||||||
|
import { client } from "../..";
|
||||||
|
import Command from "../../struct/Command";
|
||||||
|
import MessageCommandContext from "../../struct/MessageCommandContext";
|
||||||
|
import PendingLogin from "../../struct/PendingLogin";
|
||||||
|
import logger from "../logger";
|
||||||
|
import { DEFAULT_PREFIX } from "../modules/command_handler";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'login',
|
||||||
|
aliases: null,
|
||||||
|
description: 'Log into the web dashboard',
|
||||||
|
category: 'misc',
|
||||||
|
run: async (message: MessageCommandContext, args: string[]) => {
|
||||||
|
try {
|
||||||
|
const code = args.shift();
|
||||||
|
if (!code) {
|
||||||
|
return message.reply(`If you're trying to log in, you can access the dashboard `
|
||||||
|
+ `[here](${process.env.WEB_UI_URL || 'https://automod.janderedev.xyz'}).\n\n`
|
||||||
|
+ `If you already have a code, you can use \`${DEFAULT_PREFIX}login [Code]\`.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const login: FindOneResult<PendingLogin> = await client.db.get('pending_logins').findOne({
|
||||||
|
code,
|
||||||
|
user: message.author_id,
|
||||||
|
confirmed: false,
|
||||||
|
exchanged: false,
|
||||||
|
invalid: false,
|
||||||
|
expires: {
|
||||||
|
$gt: Date.now(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!login) return message.reply(`Unknown code. Make sure you're logged into the correct account.`);
|
||||||
|
|
||||||
|
if (login.requirePhishingConfirmation) {
|
||||||
|
logger.info(`Showing phishing warning to ${message.author_id}`);
|
||||||
|
await Promise.all([
|
||||||
|
message.reply(
|
||||||
|
`# If someone told you to run this, stop!\n` +
|
||||||
|
`This could give an attacker access to all servers you're using AutoMod in.\n` +
|
||||||
|
`If someone else told you to run this command, **block them and ignore this.**\n\n` +
|
||||||
|
`Otherwise, if this was you trying to log in from <${process.env.WEB_UI_URL || 'https://automod.janderedev.xyz'}>, \n` +
|
||||||
|
`you can run this command again to continue.\n` +
|
||||||
|
`##### You're seeing this because this is the first time you're trying to log in. Stay safe!`
|
||||||
|
),
|
||||||
|
client.db.get('pending_logins').update({ _id: login._id }, { $set: { requirePhishingConfirmation: false } }),
|
||||||
|
]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await Promise.all([
|
||||||
|
message.reply(`Successfully logged in.\n\n` +
|
||||||
|
`If this wasn't you, ~~run \`${DEFAULT_PREFIX}logout ${code}\` immediately~~ pray.`),
|
||||||
|
client.db.get('pending_logins').update({ _id: login._id }, { $set: { confirmed: true } }),
|
||||||
|
]);
|
||||||
|
} catch(e) {
|
||||||
|
console.error(e);
|
||||||
|
message.reply(`An error occurred: ${e}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} as Command;
|
64
bot/src/bot/commands/logout.ts
Normal file
64
bot/src/bot/commands/logout.ts
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
import { FindOneResult, FindResult } from "monk";
|
||||||
|
import { client } from "../..";
|
||||||
|
import Command from "../../struct/Command";
|
||||||
|
import MessageCommandContext from "../../struct/MessageCommandContext";
|
||||||
|
import PendingLogin from "../../struct/PendingLogin";
|
||||||
|
import { DEFAULT_PREFIX } from "../modules/command_handler";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'logout',
|
||||||
|
aliases: null,
|
||||||
|
description: 'Log out of sessions created with /login',
|
||||||
|
category: 'misc',
|
||||||
|
run: async (message: MessageCommandContext, args: string[]) => {
|
||||||
|
try {
|
||||||
|
const code = args.shift();
|
||||||
|
if (!code) {
|
||||||
|
return message.reply(`### No code provided.\n`
|
||||||
|
+ `You can invalidate a session by using \`${DEFAULT_PREFIX}logout [Code]\`, `
|
||||||
|
+ `or log out everywhere with \`${DEFAULT_PREFIX}logout ALL\``);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (code.toLowerCase() == 'all') {
|
||||||
|
const [ attempts, sessions ]: FindResult<any>[] = await Promise.all([
|
||||||
|
client.db.get('pending_logins').find({ user: message.author_id }),
|
||||||
|
client.db.get('sessions').find({ user: message.author_id }),
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (attempts.length == 0 && sessions.length == 0) return message.reply('There are no sessions to invalidate.');
|
||||||
|
|
||||||
|
await Promise.all([
|
||||||
|
client.db.get('pending_logins').update({ _id: { $in: attempts.map(a => a._id) } }, { $set: { invalid: true } }),
|
||||||
|
client.db.get('sessions').update({ _id: { $in: sessions.map(a => a._id) } }, { $set: { invalid: true } }),
|
||||||
|
]);
|
||||||
|
|
||||||
|
message.reply(`Successfully invalidated ${attempts.length} codes and ${sessions.length} sessions.`);
|
||||||
|
} else {
|
||||||
|
const loginAttempt: FindOneResult<PendingLogin> = await client.db.get('pending_logins')
|
||||||
|
.findOne({
|
||||||
|
code: code.toUpperCase(),
|
||||||
|
user: message.author_id,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!loginAttempt || loginAttempt.invalid) {
|
||||||
|
return message.reply('That code doesn\'t seem to exist.');
|
||||||
|
}
|
||||||
|
|
||||||
|
await client.db.get('pending_logins').update({ _id: loginAttempt._id }, { $set: { invalid: true } });
|
||||||
|
|
||||||
|
if (loginAttempt.exchanged) {
|
||||||
|
const session: FindOneResult<any> = await client.db.get('sessions').findOne({ nonce: loginAttempt.nonce });
|
||||||
|
if (session) {
|
||||||
|
await client.db.get('sessions').update({ _id: session._id }, { $set: { invalid: true } });
|
||||||
|
return message.reply(`Successfully invalidated code and terminated associated session.`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message.reply(`Successfully invalidated code.`);
|
||||||
|
}
|
||||||
|
} catch(e) {
|
||||||
|
console.error(e);
|
||||||
|
message.reply(`An error occurred: ${e}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} as Command;
|
|
@ -4,7 +4,13 @@
|
||||||
|
|
||||||
import ws from "ws";
|
import ws from "ws";
|
||||||
import logger from "../logger";
|
import logger from "../logger";
|
||||||
|
import crypto from 'crypto';
|
||||||
|
import { client as bot } from '../..';
|
||||||
import { EventEmitter } from "events";
|
import { EventEmitter } from "events";
|
||||||
|
import { parseUser } from "../util";
|
||||||
|
import PendingLogin from "../../struct/PendingLogin";
|
||||||
|
import { LogLevel } from "log75";
|
||||||
|
import { ulid } from "ulid";
|
||||||
|
|
||||||
const wsEvents = new EventEmitter();
|
const wsEvents = new EventEmitter();
|
||||||
const { API_WS_URL, API_WS_TOKEN } = process.env;
|
const { API_WS_URL, API_WS_TOKEN } = process.env;
|
||||||
|
@ -50,7 +56,24 @@ function connect() {
|
||||||
client.on('message', (msg) => {
|
client.on('message', (msg) => {
|
||||||
logger.debug(`[WS] [<] ${msg.toString('utf8')}`);
|
logger.debug(`[WS] [<] ${msg.toString('utf8')}`);
|
||||||
try {
|
try {
|
||||||
wsEvents.emit('message', JSON.parse(msg.toString('utf8')));
|
const jsonMsg = JSON.parse(msg.toString('utf8'));
|
||||||
|
wsEvents.emit('message', jsonMsg);
|
||||||
|
if (jsonMsg['nonce'] && jsonMsg['type']) {
|
||||||
|
const hasListeners = wsEvents.emit(`req:${jsonMsg.type}`, jsonMsg.data, (res: { [key: string]: any }) => {
|
||||||
|
wsSend({ nonce: jsonMsg.nonce, type: `response:${jsonMsg.nonce}`, data: res });
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!hasListeners) {
|
||||||
|
wsSend({
|
||||||
|
nonce: jsonMsg.nonce,
|
||||||
|
type: `response:${jsonMsg.nonce}`,
|
||||||
|
data: {
|
||||||
|
success: false,
|
||||||
|
error: 'No event listeners available for event'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
} catch(e) { console.error(e) }
|
} catch(e) { console.error(e) }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -65,6 +88,46 @@ function wsSend(data: { [key: string]: any }) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setInterval(() => wsSend({ "among": "us" }), 1000);
|
wsEvents.on('req:test', (data: any, res: (data: any) => void) => {
|
||||||
|
res({ received: data });
|
||||||
|
});
|
||||||
|
|
||||||
|
wsEvents.on('req:requestLogin', async (data: any, cb: (data: any) => void) => {
|
||||||
|
try {
|
||||||
|
const user = await parseUser(data.user);
|
||||||
|
if (!user)
|
||||||
|
return cb({ success: false, statusCode: 404, error: `The specified user could not be found` });
|
||||||
|
|
||||||
|
let code: string|null = null;
|
||||||
|
while (!code) {
|
||||||
|
const c = crypto.randomBytes(8).toString('hex');
|
||||||
|
const found = await bot.db.get('pending_logins').find({ code: c, user: user._id, confirmed: false });
|
||||||
|
if (found.length > 0) continue;
|
||||||
|
code = c.substring(0, 8).toUpperCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info(`Attempted login for user ${user._id} with code ${code}`);
|
||||||
|
|
||||||
|
const nonce = ulid();
|
||||||
|
|
||||||
|
const previousLogins = await bot.db.get('pending_logins').find({ user: user._id, confirmed: true });
|
||||||
|
|
||||||
|
await bot.db.get('pending_logins').insert({
|
||||||
|
code,
|
||||||
|
expires: Date.now() + (1000 * 60 * 15), // Expires in 15 minutes
|
||||||
|
user: user._id,
|
||||||
|
nonce: nonce,
|
||||||
|
confirmed: false,
|
||||||
|
requirePhishingConfirmation: previousLogins.length == 0,
|
||||||
|
exchanged: false,
|
||||||
|
invalid: false,
|
||||||
|
} as PendingLogin);
|
||||||
|
|
||||||
|
cb({ success: true, uid: user._id, nonce, code });
|
||||||
|
} catch(e) {
|
||||||
|
console.error(e);
|
||||||
|
cb({ success: false, error: e });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
export { wsEvents, wsSend }
|
export { wsEvents, wsSend }
|
||||||
|
|
12
bot/src/struct/PendingLogin.ts
Normal file
12
bot/src/struct/PendingLogin.ts
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
class PendingLogin {
|
||||||
|
user: string;
|
||||||
|
code: string;
|
||||||
|
expires: number;
|
||||||
|
nonce: string;
|
||||||
|
confirmed: boolean;
|
||||||
|
requirePhishingConfirmation: boolean;
|
||||||
|
exchanged: boolean;
|
||||||
|
invalid: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PendingLogin;
|
Loading…
Reference in a new issue