From 39ccd942cb6a3dad737d7192bc2ad94c9806c499 Mon Sep 17 00:00:00 2001 From: Jonathan Barrow Date: Sun, 19 Mar 2023 16:21:30 -0400 Subject: [PATCH] Convert Maps to JSON objects on request body --- src/middleware/xml-parser.ts | 3 +- src/services/nnid/routes/miis.ts | 2 - src/services/nnid/routes/people.ts | 85 +++++++++++++++-------------- src/services/nnid/routes/support.ts | 2 +- src/types/services/nnid/person.ts | 4 +- src/util.ts | 4 ++ 6 files changed, 53 insertions(+), 47 deletions(-) diff --git a/src/middleware/xml-parser.ts b/src/middleware/xml-parser.ts index 31bb8b4..4f639d5 100644 --- a/src/middleware/xml-parser.ts +++ b/src/middleware/xml-parser.ts @@ -1,7 +1,7 @@ import express from 'express'; import xmlbuilder from 'xmlbuilder'; import { document as xmlParser } from 'xmlbuilder2'; -import { getValueFromHeaders } from '@/util'; +import { getValueFromHeaders, mapToObject } from '@/util'; function XMLMiddleware(request: express.Request, response: express.Response, next: express.NextFunction): void { if (request.method == 'POST' || request.method == 'PUT') { @@ -32,6 +32,7 @@ function XMLMiddleware(request: express.Request, response: express.Response, nex try { request.body = xmlParser(body); request.body = request.body.toObject(); + request.body = mapToObject(request.body); } catch (error) { response.status(401); diff --git a/src/services/nnid/routes/miis.ts b/src/services/nnid/routes/miis.ts index 4333e29..dd58978 100644 --- a/src/services/nnid/routes/miis.ts +++ b/src/services/nnid/routes/miis.ts @@ -121,8 +121,6 @@ router.get('/', async (request: express.Request, response: express.Response) => }); } - //console.log(results[0].mii.data.replace(/(\r\n|\n|\r)/gm, '')); - response.send(xmlbuilder.create({ miis: { mii: miis diff --git a/src/services/nnid/routes/people.ts b/src/services/nnid/routes/people.ts index 8f48453..a1c271e 100644 --- a/src/services/nnid/routes/people.ts +++ b/src/services/nnid/routes/people.ts @@ -69,10 +69,9 @@ router.post('/', ratelimit, deviceCertificateMiddleware, async (request: express }).end()); } - // * request.body is a Map, as is request.body.person - const person: Person = request.body.get('person'); + const person: Person = request.body.person; - const userExists: boolean = await doesPNIDExist(person.get('user_id')); + const userExists: boolean = await doesPNIDExist(person.user_id); if (userExists) { response.status(400); @@ -111,12 +110,12 @@ router.post('/', ratelimit, deviceCertificateMiddleware, async (request: express await nexAccount.save({ session }); - const primaryPasswordHash: string = nintendoPasswordHash(person.get('password'), nexAccount.pid); + const primaryPasswordHash: string = nintendoPasswordHash(person.password, nexAccount.pid); const passwordHash: string = await bcrypt.hash(primaryPasswordHash, 10); - const countryCode: string = person.get('country'); - const language: string = person.get('language'); - const timezoneName: string = person.get('tz_name'); + const countryCode: string = person.country; + const language: string = person.language; + const timezoneName: string = person.tz_name; const regionLanguages: RegionLanguages = timezones[countryCode as keyof typeof timezones]; const regionTimezones: RegionTimezones = regionLanguages[language] ? regionLanguages[language] : Object.values(regionLanguages)[0]; @@ -137,30 +136,30 @@ router.post('/', ratelimit, deviceCertificateMiddleware, async (request: express pid: nexAccount.pid, creation_date: creationDate, updated: creationDate, - username: person.get('user_id'), - usernameLower: person.get('user_id').toLowerCase(), + username: person.user_id, + usernameLower: person.user_id.toLowerCase(), password: passwordHash, - birthdate: person.get('birth_date'), - gender: person.get('gender'), + birthdate: person.birth_date, + gender: person.gender, country: countryCode, language: language, email: { - address: person.get('email').get('address').toLowerCase(), - primary: person.get('email').get('primary') === 'Y', - parent: person.get('email').get('parent') === 'Y', + address: person.email.address.toLowerCase(), + primary: person.email.primary === 'Y', + parent: person.email.parent === 'Y', reachable: false, - validated: person.get('email').get('validated') === 'Y', + validated: person.email.validated === 'Y', id: crypto.randomBytes(4).readUInt32LE() }, - region: person.get('region'), + region: person.region, timezone: { name: timezoneName, offset: Number(timezone.utc_offset) }, mii: { - name: person.get('mii').get('name'), - primary: person.get('mii').get('name') === 'Y', - data: person.get('mii').get('data'), + name: person.mii.name, + primary: person.mii.name === 'Y', + data: person.mii.data, id: crypto.randomBytes(4).readUInt32LE(), hash: crypto.randomBytes(7).toString('hex'), image_url: '', // deprecated, will be removed in the future @@ -168,8 +167,8 @@ router.post('/', ratelimit, deviceCertificateMiddleware, async (request: express }, flags: { active: true, - marketing: person.get('marketing_flag') === 'Y', - off_device: person.get('off_device_flag') === 'Y' + marketing: person.marketing_flag === 'Y', + off_device: person.off_device_flag === 'Y' }, identification: { email_code: 1, // will be overwritten before saving @@ -456,14 +455,17 @@ router.put('/@me/miis/@primary', async (request: express.Request, response: expr }).end()); } - // TODO - Make this more strictly typed? - const mii: Map = request.body.get('mii'); + const mii: { + name: string; + primary: string; + data: string; + } = request.body.mii; // TODO - Better checks - const name: string | undefined = mii.get('name') || ''; - const primary: string | undefined = mii.get('primary') || ''; - const data: string | undefined = mii.get('data') || ''; + const name: string = mii.name; + const primary: string = mii.primary; + const data: string = mii.data; await pnid.updateMii({ name, primary, data }); @@ -531,7 +533,7 @@ router.put('/@me/deletion', async (request: express.Request, response: express.R */ router.put('/@me', async (request: express.Request, response: express.Response) => { const pnid: HydratedPNIDDocument | null = request.pnid; - const person: Person = request.body.get('person'); + const person: Person = request.body.person; if (!pnid) { response.status(400); @@ -547,13 +549,13 @@ router.put('/@me', async (request: express.Request, response: express.Response) }).end()); } - const gender: string = person.get('gender') ? person.get('gender') : pnid.gender; - const region: string = person.get('region') ? person.get('region') : pnid.region; - const countryCode: string = person.get('country') ? person.get('country') : pnid.country; - const language: string = person.get('language') ? person.get('language') : pnid.language; - const timezoneName: string = person.get('tz_name') ? person.get('tz_name') : pnid.timezone.name; - const marketingFlag: boolean = person.get('marketing_flag') ? person.get('marketing_flag') === 'Y' : pnid.flags.marketing; - const offDeviceFlag: boolean = person.get('off_device_flag') ? person.get('off_device_flag') === 'Y' : pnid.flags.off_device; + const gender: string = person.gender ? person.gender : pnid.gender; + const region: number = person.region ? person.region : pnid.region; + const countryCode: string = person.country ? person.country : pnid.country; + const language: string = person.language ? person.language : pnid.language; + const timezoneName: string = person.tz_name ? person.tz_name : pnid.timezone.name; + const marketingFlag: boolean = person.marketing_flag ? person.marketing_flag === 'Y' : pnid.flags.marketing; + const offDeviceFlag: boolean = person.off_device_flag ? person.off_device_flag === 'Y' : pnid.flags.off_device; const regionLanguages: RegionLanguages = timezones[countryCode as keyof typeof timezones]; const regionTimezones: RegionTimezones = regionLanguages[language] ? regionLanguages[language] : Object.values(regionLanguages)[0]; @@ -570,15 +572,15 @@ router.put('/@me', async (request: express.Request, response: express.Response) }; } - if (person.get('password')) { - const primaryPasswordHash: string = nintendoPasswordHash(person.get('password'), pnid.pid); + if (person.password) { + const primaryPasswordHash: string = nintendoPasswordHash(person.password, pnid.pid); const passwordHash: string = await bcrypt.hash(primaryPasswordHash, 10); pnid.password = passwordHash; } pnid.gender = gender; - pnid.region = Number(region); + pnid.region = region; pnid.timezone.name = timezoneName; pnid.timezone.offset = Number(timezone.utc_offset); pnid.timezone.marketing = marketingFlag; @@ -638,10 +640,11 @@ router.get('/@me/emails', async (request: express.Request, response: express.Res router.put('/@me/emails/@primary', async (request: express.Request, response: express.Response) => { const pnid: HydratedPNIDDocument | null = request.pnid; - // TODO - Make this more strictly typed? - const email: Map = request.body.get('email'); + const email: { + address: string; + } = request.body.email; - if (!pnid || !email) { + if (!pnid || !email || !email.address) { response.status(400); return response.end(xmlbuilder.create({ @@ -656,7 +659,7 @@ router.put('/@me/emails/@primary', async (request: express.Request, response: ex } // TODO - Better email check - pnid.email.address = (email.get('address') || '').toLowerCase(); + pnid.email.address = email.address.toLowerCase(); pnid.email.reachable = false; pnid.email.validated = false; pnid.email.validated_date = ''; diff --git a/src/services/nnid/routes/support.ts b/src/services/nnid/routes/support.ts index a8e4811..be795d6 100644 --- a/src/services/nnid/routes/support.ts +++ b/src/services/nnid/routes/support.ts @@ -14,7 +14,7 @@ const router: express.Router = express.Router(); * Description: Verifies a provided email address is valid */ router.post('/validate/email', async (request: express.Request, response: express.Response) => { - const email: string = request.body; + const email: string = request.body.email; if (!email) { return response.send(xmlbuilder.create({ diff --git a/src/types/services/nnid/person.ts b/src/types/services/nnid/person.ts index 9da3757..00958e5 100644 --- a/src/types/services/nnid/person.ts +++ b/src/types/services/nnid/person.ts @@ -2,7 +2,7 @@ import { YesNoBoolString } from '@/types/common/yes-no-bool-string'; import { IDeviceAttribute } from '@/types/mongoose/device-attribute'; // TODO - Change this Map type to something more strongly typed -export interface Person extends Map { +export interface Person { birth_date: string; user_id: string; password: string; @@ -35,7 +35,7 @@ export interface Person extends Map { approval_id: string; }; gender: string; - region: string; + region: number; marketing_flag: YesNoBoolString; device_attributes: { device_attribute: IDeviceAttribute[] diff --git a/src/util.ts b/src/util.ts index 32db6da..d43d14a 100644 --- a/src/util.ts +++ b/src/util.ts @@ -374,4 +374,8 @@ export function getValueFromHeaders(headers: IncomingHttpHeaders, key: string): } return value; +} + +export function mapToObject(map: Map): object { + return Object.fromEntries(Array.from(map.entries(), ([ k, v ]) => v instanceof Map ? [ k, mapToObject(v) ] : [ k, v ])); } \ No newline at end of file