mirror of
https://github.com/PretendoNetwork/account.git
synced 2026-09-30 14:18:22 -05:00
Handle arrays and proper types on headers and query strings
This commit is contained in:
64
package-lock.json
generated
64
package-lock.json
generated
@@ -33,6 +33,7 @@
|
||||
"nodemailer": "^6.4.2",
|
||||
"redis": "^4.3.1",
|
||||
"tga": "^1.0.4",
|
||||
"typescript-is": "^0.19.0",
|
||||
"validator": "^13.7.0",
|
||||
"xmlbuilder": "^13.0.2",
|
||||
"xmlbuilder2": "0.0.4"
|
||||
@@ -48,6 +49,7 @@
|
||||
"@types/node": "^18.14.4",
|
||||
"@types/node-rsa": "^1.1.1",
|
||||
"@types/nodemailer": "^6.4.7",
|
||||
"@types/qs": "^6.9.7",
|
||||
"@types/validator": "^13.7.14",
|
||||
"@typescript-eslint/eslint-plugin": "^5.54.1",
|
||||
"@typescript-eslint/parser": "^5.54.1",
|
||||
@@ -5465,6 +5467,11 @@
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/nested-error-stacks": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/nested-error-stacks/-/nested-error-stacks-2.1.1.tgz",
|
||||
"integrity": "sha512-9iN1ka/9zmX1ZvLV9ewJYEk9h7RyRRtqdK0woXcqohu8EWIerfPUjYJPg0ULy0UqP7cslmdGc8xKDJcojlKiaw=="
|
||||
},
|
||||
"node_modules/next-tick": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz",
|
||||
@@ -5976,6 +5983,12 @@
|
||||
"@redis/time-series": "1.0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/reflect-metadata": {
|
||||
"version": "0.1.13",
|
||||
"resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz",
|
||||
"integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/regexpp": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz",
|
||||
@@ -6679,14 +6692,12 @@
|
||||
"node_modules/tslib": {
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
|
||||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
|
||||
"devOptional": true
|
||||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
||||
},
|
||||
"node_modules/tsutils": {
|
||||
"version": "3.21.0",
|
||||
"resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz",
|
||||
"integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"tslib": "^1.8.1"
|
||||
},
|
||||
@@ -6762,7 +6773,6 @@
|
||||
"version": "4.9.5",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
|
||||
"integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
@@ -6771,6 +6781,24 @@
|
||||
"node": ">=4.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/typescript-is": {
|
||||
"version": "0.19.0",
|
||||
"resolved": "https://registry.npmjs.org/typescript-is/-/typescript-is-0.19.0.tgz",
|
||||
"integrity": "sha512-SAJEx2cxbQZhfOjDEjPnQJt1qRS1M3wrKbUwvsywVHWGbMgM1dcIf9gPWNDS1/dgTa/7Iexk2mmAHHsP9MeCsA==",
|
||||
"dependencies": {
|
||||
"nested-error-stacks": "^2",
|
||||
"tsutils": "^3.17.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.14.4"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"reflect-metadata": ">=0.1.12"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": "^4.1.5"
|
||||
}
|
||||
},
|
||||
"node_modules/universalify": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
|
||||
@@ -11517,6 +11545,11 @@
|
||||
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
|
||||
"integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="
|
||||
},
|
||||
"nested-error-stacks": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/nested-error-stacks/-/nested-error-stacks-2.1.1.tgz",
|
||||
"integrity": "sha512-9iN1ka/9zmX1ZvLV9ewJYEk9h7RyRRtqdK0woXcqohu8EWIerfPUjYJPg0ULy0UqP7cslmdGc8xKDJcojlKiaw=="
|
||||
},
|
||||
"next-tick": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz",
|
||||
@@ -11892,6 +11925,12 @@
|
||||
"@redis/time-series": "1.0.4"
|
||||
}
|
||||
},
|
||||
"reflect-metadata": {
|
||||
"version": "0.1.13",
|
||||
"resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz",
|
||||
"integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==",
|
||||
"optional": true
|
||||
},
|
||||
"regexpp": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz",
|
||||
@@ -12440,14 +12479,12 @@
|
||||
"tslib": {
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
|
||||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
|
||||
"devOptional": true
|
||||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
||||
},
|
||||
"tsutils": {
|
||||
"version": "3.21.0",
|
||||
"resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz",
|
||||
"integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"tslib": "^1.8.1"
|
||||
}
|
||||
@@ -12501,8 +12538,17 @@
|
||||
"typescript": {
|
||||
"version": "4.9.5",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
|
||||
"integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
|
||||
"dev": true
|
||||
"integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g=="
|
||||
},
|
||||
"typescript-is": {
|
||||
"version": "0.19.0",
|
||||
"resolved": "https://registry.npmjs.org/typescript-is/-/typescript-is-0.19.0.tgz",
|
||||
"integrity": "sha512-SAJEx2cxbQZhfOjDEjPnQJt1qRS1M3wrKbUwvsywVHWGbMgM1dcIf9gPWNDS1/dgTa/7Iexk2mmAHHsP9MeCsA==",
|
||||
"requires": {
|
||||
"nested-error-stacks": "^2",
|
||||
"reflect-metadata": ">=0.1.12",
|
||||
"tsutils": "^3.17.1"
|
||||
}
|
||||
},
|
||||
"universalify": {
|
||||
"version": "0.1.2",
|
||||
|
||||
@@ -49,6 +49,7 @@
|
||||
"nodemailer": "^6.4.2",
|
||||
"redis": "^4.3.1",
|
||||
"tga": "^1.0.4",
|
||||
"typescript-is": "^0.19.0",
|
||||
"validator": "^13.7.0",
|
||||
"xmlbuilder": "^13.0.2",
|
||||
"xmlbuilder2": "0.0.4"
|
||||
@@ -64,6 +65,7 @@
|
||||
"@types/node": "^18.14.4",
|
||||
"@types/node-rsa": "^1.1.1",
|
||||
"@types/nodemailer": "^6.4.7",
|
||||
"@types/qs": "^6.9.7",
|
||||
"@types/validator": "^13.7.14",
|
||||
"@typescript-eslint/eslint-plugin": "^5.54.1",
|
||||
"@typescript-eslint/parser": "^5.54.1",
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import express from 'express';
|
||||
import { getValueFromHeaders } from '@/util';
|
||||
import { getUserBearer } from '@/database';
|
||||
import { HydratedPNIDDocument } from '@/types/mongoose/pnid';
|
||||
|
||||
async function APIMiddleware(request: express.Request, _response: express.Response, next: express.NextFunction): Promise<void> {
|
||||
const authHeader: string | undefined = request.headers.authorization;
|
||||
const authHeader: string | undefined = getValueFromHeaders(request.headers, 'authorization');
|
||||
|
||||
if (!authHeader || !(authHeader.startsWith('Bearer'))) {
|
||||
return next();
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import express from 'express';
|
||||
import xmlbuilder from 'xmlbuilder';
|
||||
import { getValueFromHeaders } from '@/util';
|
||||
|
||||
const VALID_CLIENT_ID_SECRET_PAIRS: { [key: string]: string } = {
|
||||
// * 'Key' is the client ID, 'Value' is the client secret
|
||||
@@ -14,8 +15,8 @@ function nintendoClientHeaderCheck(request: express.Request, response: express.R
|
||||
response.set('Server', 'Nintendo 3DS (http)');
|
||||
response.set('X-Nintendo-Date', new Date().getTime().toString());
|
||||
|
||||
const clientId: string = request.headers['x-nintendo-client-id'] as string;
|
||||
const clientSecret: string = request.headers['x-nintendo-client-secret'] as string;
|
||||
const clientId: string | undefined = getValueFromHeaders(request.headers, 'x-nintendo-client-id');
|
||||
const clientSecret: string | undefined = getValueFromHeaders(request.headers, 'x-nintendo-client-secret');
|
||||
|
||||
if (
|
||||
!clientId ||
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import express from 'express';
|
||||
import NintendoCertificate from '@/nintendo-certificate';
|
||||
import { getValueFromHeaders } from '@/util';
|
||||
|
||||
function deviceCertificateMiddleware(request: express.Request, _response: express.Response, next: express.NextFunction): void {
|
||||
const certificate: string = request.headers['x-nintendo-device-cert'] as string;
|
||||
const certificate: string | undefined = getValueFromHeaders(request.headers, 'x-nintendo-device-cert');
|
||||
|
||||
if (!certificate) {
|
||||
return next();
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import express from 'express';
|
||||
import xmlbuilder from 'xmlbuilder';
|
||||
import { getValueFromHeaders } from '@/util';
|
||||
import { getUserBasic, getUserBearer } from '@/database';
|
||||
import { HydratedPNIDDocument } from '@/types/mongoose/pnid';
|
||||
|
||||
async function PNIDMiddleware(request: express.Request, response: express.Response, next: express.NextFunction): Promise<void> {
|
||||
const authHeader: string | undefined = request.headers.authorization;
|
||||
const authHeader: string | undefined = getValueFromHeaders(request.headers, 'authorization');
|
||||
|
||||
if (!authHeader || !(authHeader.startsWith('Bearer') || authHeader.startsWith('Basic'))) {
|
||||
return next();
|
||||
|
||||
@@ -1,12 +1,18 @@
|
||||
import crypto from 'node:crypto';
|
||||
import express from 'express';
|
||||
import ratelimit from 'express-rate-limit';
|
||||
import { getValueFromHeaders } from '@/util';
|
||||
|
||||
export default ratelimit({
|
||||
windowMs: 60 * 1000,
|
||||
max: 1,
|
||||
keyGenerator: (request: express.Request) => {
|
||||
const data: string = request.headers['x-nintendo-device-cert'] as string;
|
||||
let data: string | undefined = getValueFromHeaders(request.headers, 'x-nintendo-device-cert');
|
||||
|
||||
if (!data) {
|
||||
data = request.ip;
|
||||
}
|
||||
|
||||
return crypto.createHash('md5').update(data).digest('hex');
|
||||
}
|
||||
});
|
||||
@@ -1,11 +1,12 @@
|
||||
import express from 'express';
|
||||
import xmlbuilder from 'xmlbuilder';
|
||||
import { document as xmlParser } from 'xmlbuilder2';
|
||||
import { getValueFromHeaders } from '@/util';
|
||||
|
||||
function XMLMiddleware(request: express.Request, response: express.Response, next: express.NextFunction): void {
|
||||
if (request.method == 'POST' || request.method == 'PUT') {
|
||||
const contentType: string | undefined = request.headers['content-type'];
|
||||
const contentLength: string | undefined = request.headers['content-length'];
|
||||
const contentType: string | undefined = getValueFromHeaders(request.headers, 'content-type');
|
||||
const contentLength: string | undefined = getValueFromHeaders(request.headers, 'content-length');
|
||||
let body: string = '';
|
||||
|
||||
if (
|
||||
|
||||
@@ -42,7 +42,7 @@ const CTR_LFCS_B_PUB = Buffer.from([
|
||||
]);
|
||||
|
||||
// Signature options
|
||||
const SIGNATURE_SIZES: { [key: string]: SignatureSize } = {
|
||||
const SIGNATURE_SIZES = {
|
||||
RSA_4096_SHA1: <SignatureSize>{
|
||||
SIZE: 0x200,
|
||||
PADDING_SIZE: 0x3C
|
||||
@@ -67,7 +67,7 @@ const SIGNATURE_SIZES: { [key: string]: SignatureSize } = {
|
||||
SIZE: 0x3C,
|
||||
PADDING_SIZE: 0x40
|
||||
}
|
||||
};
|
||||
} as const;
|
||||
|
||||
class NintendoCertificate {
|
||||
_certificate: Buffer;
|
||||
|
||||
@@ -9,7 +9,7 @@ import morgan from 'morgan';
|
||||
import xmlparser from '@/middleware/xml-parser';
|
||||
import { connect as connectCache } from '@/cache';
|
||||
import { connect as connectDatabase } from '@/database';
|
||||
import { fullUrl } from '@/util';
|
||||
import { fullUrl, getValueFromHeaders } from '@/util';
|
||||
import { LOG_INFO, LOG_SUCCESS, LOG_WARN } from '@/logger';
|
||||
|
||||
import conntest from '@/services/conntest';
|
||||
@@ -49,7 +49,11 @@ app.use(assets);
|
||||
LOG_INFO('Creating 404 status handler');
|
||||
app.use((request: express.Request, response: express.Response) => {
|
||||
const url: string = fullUrl(request);
|
||||
const deviceId: string = request.headers['X-Nintendo-Device-ID'] as string || 'Unknown';
|
||||
let deviceId: string | undefined = getValueFromHeaders(request.headers, 'X-Nintendo-Device-ID');
|
||||
|
||||
if (!deviceId) {
|
||||
deviceId = 'Unknown';
|
||||
}
|
||||
|
||||
LOG_WARN(`HTTP 404 at ${url} from ${deviceId}`);
|
||||
|
||||
@@ -66,7 +70,11 @@ LOG_INFO('Creating non-404 status handler');
|
||||
app.use((error: any, request: express.Request, response: express.Response, _next: express.NextFunction) => {
|
||||
const status: number = error.status || 500;
|
||||
const url: string = fullUrl(request);
|
||||
const deviceId: string = request.headers['X-Nintendo-Device-ID'] as string || 'Unknown';
|
||||
let deviceId: string | undefined = getValueFromHeaders(request.headers, 'X-Nintendo-Device-ID');
|
||||
|
||||
if (!deviceId) {
|
||||
deviceId = 'Unknown';
|
||||
}
|
||||
|
||||
LOG_WARN(`HTTP ${status} at ${url} from ${deviceId}: ${error.message}`);
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import express from 'express';
|
||||
import moment from 'moment';
|
||||
import { PNID } from '@/models/pnid';
|
||||
import { sendEmailConfirmedEmail } from '@/util';
|
||||
import { getValueFromQueryString, sendEmailConfirmedEmail } from '@/util';
|
||||
import { HydratedPNIDDocument } from '@/types/mongoose/pnid';
|
||||
|
||||
const router: express.Router = express.Router();
|
||||
|
||||
router.get('/verify', async (request: express.Request, response: express.Response) => {
|
||||
const token: string = request.query.token as string;
|
||||
const token: string | undefined = getValueFromQueryString(request.query, 'token');
|
||||
|
||||
if (!token || token.trim() == '') {
|
||||
return response.status(400).json({
|
||||
|
||||
@@ -4,7 +4,7 @@ import { getLocalCDNFile } from '@/cache';
|
||||
const router: express.Router = express.Router();
|
||||
|
||||
router.get('/*', async (request: express.Request, response: express.Response) => {
|
||||
const filePath: string = request.params[0] as string;
|
||||
const filePath: string = request.params[0];
|
||||
|
||||
const file: Buffer = await getLocalCDNFile(filePath);
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import express from 'express';
|
||||
import xmlbuilder from 'xmlbuilder';
|
||||
import { getValueFromQueryString } from '@/util';
|
||||
import { PNID } from '@/models/pnid';
|
||||
import { HydratedPNIDDocument } from '@/types/mongoose/pnid';
|
||||
|
||||
@@ -11,19 +12,26 @@ const router: express.Router = express.Router();
|
||||
* Description: Maps between NNID usernames and PIDs
|
||||
*/
|
||||
router.get('/mapped_ids', async (request: express.Request, response: express.Response) => {
|
||||
const inputType: string = request.query.input_type as string;
|
||||
const outputType: string = request.query.output_type as string;
|
||||
let inputList: string[];
|
||||
const inputType: string | undefined = getValueFromQueryString(request.query, 'input_type');
|
||||
const outputType: string | undefined = getValueFromQueryString(request.query, 'output_type');
|
||||
const input: string | undefined = getValueFromQueryString(request.query, 'input');
|
||||
|
||||
if (!inputType || !outputType || !input) {
|
||||
return response.status(400).send(xmlbuilder.create({
|
||||
errors: {
|
||||
error: {
|
||||
cause: 'Bad Request',
|
||||
code: '1600',
|
||||
message: 'Unable to process request'
|
||||
}
|
||||
}
|
||||
}).end());
|
||||
}
|
||||
|
||||
let inputList: string[] = input.split(',');
|
||||
let queryInput: string;
|
||||
let queryOutput: string;
|
||||
|
||||
if (Array.isArray(request.query.input)) {
|
||||
inputList = request.query.input as string[];
|
||||
} else {
|
||||
const input = request.query.input as string;
|
||||
inputList = input.split(',');
|
||||
}
|
||||
|
||||
inputList = inputList.filter(input => input); // * Remove null inputs
|
||||
|
||||
if (inputType === 'user_id') {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import express from 'express';
|
||||
import xmlbuilder from 'xmlbuilder';
|
||||
import { getValueFromQueryString } from '@/util';
|
||||
import { PNID } from '@/models/pnid';
|
||||
import { config } from '@/config-manager';
|
||||
import { HydratedPNIDDocument } from '@/types/mongoose/pnid';
|
||||
@@ -13,15 +14,22 @@ const router: express.Router = express.Router();
|
||||
* Description: Returns a list of NNID miis
|
||||
*/
|
||||
router.get('/', async (request: express.Request, response: express.Response) => {
|
||||
let pids: number[];
|
||||
const input: string | undefined = getValueFromQueryString(request.query, 'pids');
|
||||
|
||||
if (Array.isArray(request.query.pids)) {
|
||||
pids = request.query.pids.map(pid => Number(pid));
|
||||
} else {
|
||||
const input = request.query.pids as string;
|
||||
pids = input.split(',').map(pid => Number(pid));
|
||||
if (!input) {
|
||||
return response.status(400).send(xmlbuilder.create({
|
||||
errors: {
|
||||
error: {
|
||||
cause: 'Bad Request',
|
||||
code: '1600',
|
||||
message: 'Unable to process request'
|
||||
}
|
||||
}
|
||||
}).end());
|
||||
}
|
||||
|
||||
const pids: number[] = input.split(',').map(pid => Number(pid));
|
||||
|
||||
const results: HydratedPNIDDocument[] = await PNID.where('pid', pids);
|
||||
const miis: {
|
||||
data: string;
|
||||
|
||||
@@ -7,7 +7,7 @@ import mongoose from 'mongoose';
|
||||
import deviceCertificateMiddleware from '@/middleware/device-certificate';
|
||||
import ratelimit from '@/middleware/ratelimit';
|
||||
import { connection as databaseConnection, doesUserExist, getUserProfileJSONByPID } from '@/database';
|
||||
import { nintendoPasswordHash, sendConfirmationEmail } from '@/util';
|
||||
import { getValueFromHeaders, nintendoPasswordHash, sendConfirmationEmail } from '@/util';
|
||||
import { PNID } from '@/models/pnid';
|
||||
import { NEXAccount } from '@/models/nex-account';
|
||||
import { LOG_ERROR } from '@/logger';
|
||||
@@ -292,12 +292,25 @@ router.get('/@me/devices', async (request: express.Request, response: express.Re
|
||||
response.set('X-Nintendo-Date', new Date().getTime().toString());
|
||||
|
||||
const pnid: HydratedPNIDDocument | null = request.pnid;
|
||||
const deviceId: string = request.headers['x-nintendo-device-id'] as string;
|
||||
const acceptLanguage: string = request.headers['accept-language'] as string;
|
||||
const platformId: string = request.headers['x-nintendo-platform-id'] as string;
|
||||
const region: string = request.headers['x-nintendo-region'] as string;
|
||||
const serialNumber: string = request.headers['x-nintendo-serial-number'] as string;
|
||||
const systemVersion: string = request.headers['x-nintendo-system-version'] as string;
|
||||
const deviceId: string | undefined = getValueFromHeaders(request.headers, 'x-nintendo-device-id');
|
||||
const acceptLanguage: string | undefined = getValueFromHeaders(request.headers, 'accept-language');
|
||||
const platformId: string | undefined = getValueFromHeaders(request.headers, 'x-nintendo-platform-id');
|
||||
const region: string | undefined = getValueFromHeaders(request.headers, 'x-nintendo-region');
|
||||
const serialNumber: string | undefined = getValueFromHeaders(request.headers, 'x-nintendo-serial-number');
|
||||
const systemVersion: string | undefined = getValueFromHeaders(request.headers, 'x-nintendo-system-version');
|
||||
|
||||
if (!deviceId || !acceptLanguage || !platformId || !region || !serialNumber || !systemVersion) {
|
||||
// TODO - Research these error more
|
||||
return response.status(400).send(xmlbuilder.create({
|
||||
errors: {
|
||||
error: {
|
||||
cause: 'Bad Request',
|
||||
code: '1600',
|
||||
message: 'Unable to process request'
|
||||
}
|
||||
}
|
||||
}).end());
|
||||
}
|
||||
|
||||
if (!pnid) {
|
||||
// TODO - Research this error more
|
||||
|
||||
@@ -2,7 +2,7 @@ import express from 'express';
|
||||
import xmlbuilder from 'xmlbuilder';
|
||||
import fs from 'fs-extra';
|
||||
import { getServerByTitleId, getServer } from '@/database';
|
||||
import { generateToken } from '@/util';
|
||||
import { generateToken, getValueFromHeaders, getValueFromQueryString } from '@/util';
|
||||
import { getServicePublicKey, getServiceSecretKey, getNEXPublicKey, getNEXSecretKey } from '@/cache';
|
||||
import { NEXAccount } from '@/models/nex-account';
|
||||
import { CryptoOptions } from '@/types/common/crypto-options';
|
||||
@@ -35,7 +35,20 @@ router.get('/service_token/@me', async (request: express.Request, response: expr
|
||||
}).end());
|
||||
}
|
||||
|
||||
const titleId: string = request.headers['x-nintendo-title-id'] as string;
|
||||
const titleId: string | undefined = getValueFromHeaders(request.headers, 'x-nintendo-title-id');
|
||||
|
||||
if (!titleId) {
|
||||
// TODO - Research this error more
|
||||
return response.send(xmlbuilder.create({
|
||||
errors: {
|
||||
error: {
|
||||
code: '1021',
|
||||
message: 'The requested game server was not found'
|
||||
}
|
||||
}
|
||||
}).end());
|
||||
}
|
||||
|
||||
const serverAccessLevel: string = pnid.get('server_access_level');
|
||||
const server: HydratedServerDocument | null = await getServerByTitleId(titleId, serverAccessLevel);
|
||||
|
||||
@@ -121,7 +134,7 @@ router.get('/nex_token/@me', async (request: express.Request, response: express.
|
||||
}).end());
|
||||
}
|
||||
|
||||
const gameServerID: string = request.query.game_server_id as string;
|
||||
const gameServerID: string | undefined = getValueFromQueryString(request.query, 'game_server_id');
|
||||
|
||||
if (!gameServerID) {
|
||||
return response.send(xmlbuilder.create({
|
||||
@@ -152,7 +165,19 @@ router.get('/nex_token/@me', async (request: express.Request, response: express.
|
||||
const ip: string = server.ip;
|
||||
const port: number = server.port;
|
||||
const device: number = server.device;
|
||||
const titleId: string = request.headers['x-nintendo-title-id'] as string;
|
||||
const titleId: string | undefined = getValueFromHeaders(request.headers, 'x-nintendo-title-id');
|
||||
|
||||
if (!titleId) {
|
||||
// TODO - Research this error more
|
||||
return response.send(xmlbuilder.create({
|
||||
errors: {
|
||||
error: {
|
||||
code: '1021',
|
||||
message: 'The requested game server was not found'
|
||||
}
|
||||
}
|
||||
}).end());
|
||||
}
|
||||
|
||||
const cryptoPath: string = `${__dirname}/../../../../certs/nex/${serverName}`;
|
||||
|
||||
|
||||
3
src/types/common/safe-qs.ts
Normal file
3
src/types/common/safe-qs.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export interface SafeQs {
|
||||
[key: string]: string | undefined
|
||||
}
|
||||
54
src/util.ts
54
src/util.ts
@@ -5,6 +5,7 @@ import aws from 'aws-sdk';
|
||||
import fs from 'fs-extra';
|
||||
import express from 'express';
|
||||
import mongoose from 'mongoose';
|
||||
import { ParsedQs } from 'qs';
|
||||
import { sendMail } from '@/mailer';
|
||||
import { getServiceAESKey, getServicePrivateKey, getServiceSecretKey, getServicePublicKey } from '@/cache';
|
||||
import { config, disabledFeatures } from '@/config-manager';
|
||||
@@ -13,6 +14,8 @@ import { TokenOptions } from '@/types/common/token-options';
|
||||
import { Token } from '@/types/common/token';
|
||||
import { IPNID, IPNIDMethods } from '@/types/mongoose/pnid';
|
||||
import { MailerOptions } from '@/types/common/mailer-options';
|
||||
import { SafeQs } from '@/types/common/safe-qs';
|
||||
import { IncomingHttpHeaders } from 'node:http';
|
||||
|
||||
let s3: aws.S3;
|
||||
|
||||
@@ -320,4 +323,55 @@ export async function sendForgotPasswordEmail(pnid: mongoose.HydratedDocument<IP
|
||||
};
|
||||
|
||||
await sendMail(mailerOptions);
|
||||
}
|
||||
|
||||
export function makeSafeQs(query: ParsedQs): SafeQs {
|
||||
const entries = Object.entries(query);
|
||||
const output: SafeQs = {};
|
||||
|
||||
for (const [key, value] of entries) {
|
||||
if (typeof value !== 'string') {
|
||||
// * ignore non-strings
|
||||
continue;
|
||||
}
|
||||
|
||||
output[key] = value;
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
export function getValueFromQueryString(qs: ParsedQs, key: string): string | undefined {
|
||||
let property: string | ParsedQs | string[] | ParsedQs[] | SafeQs | undefined = qs[key];
|
||||
let value: string | undefined;
|
||||
|
||||
if (property) {
|
||||
if (Array.isArray(property)) {
|
||||
property = property[0];
|
||||
}
|
||||
|
||||
if (typeof property !== 'string') {
|
||||
property = makeSafeQs(<ParsedQs>property);
|
||||
value = (<SafeQs>property)[key];
|
||||
} else {
|
||||
value = <string>property;
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
export function getValueFromHeaders(headers: IncomingHttpHeaders, key: string): string | undefined {
|
||||
let header: string | string[] | undefined = headers[key];
|
||||
let value: string | undefined;
|
||||
|
||||
if (header) {
|
||||
if (Array.isArray(header)) {
|
||||
header = header[0];
|
||||
}
|
||||
|
||||
value = header;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
Reference in New Issue
Block a user