Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 33 additions & 8 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
"colors": "^1.4.0",
"cors": "^2.8.5",
"crc": "^4.3.2",
"cron": "^4.4.0",
"dicer": "^0.2.5",
"dotenv": "^16.0.3",
"ejs": "^3.1.10",
Expand Down
14 changes: 14 additions & 0 deletions src/database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -339,3 +339,17 @@ export async function removePNIDConnectionDiscord(pnid: HydratedPNIDDocument): P
status: 200
};
}

export async function checkMarkedDeletions(): Promise<void> {
const pnids = await PNID.find({
marked_for_deletion: true,
deleted: false,
hard_delete_time: {
$lte: new Date()
}
});

for (const pnid of pnids) {
await pnid.scrub();
}
}
2 changes: 1 addition & 1 deletion src/middleware/pnid.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ async function PNIDMiddleware(request: express.Request, response: express.Respon
return;
}

if (pnid.deleted) {
if (pnid.deleted || pnid.marked_for_deletion) {
response.status(400).send(xmlbuilder.create({
errors: {
error: {
Expand Down
11 changes: 11 additions & 0 deletions src/models/pnid.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ const PNIDSchema = new Schema<IPNID, PNIDModel, IPNIDMethods>({
type: Boolean,
default: false
},
marked_for_deletion: {
type: Boolean,
default: false
},
hard_delete_time: Date,
permissions: {
type: BigInt,
default: 0n
Expand Down Expand Up @@ -225,6 +230,11 @@ PNIDSchema.method('generateMiiImages', async function generateMiiImages(): Promi
await uploadCDNAsset(config.s3.bucket, `${userMiiKey}/body.png`, miiStudioBodyImageData, 'public-read');
});

PNIDSchema.method('markForDeletion', async function markForDeletion() {
this.marked_for_deletion = true;
this.hard_delete_time = new Date(Date.now() + (7 * 24 * 3600 * 1000)); // * 7 day grace period
});

PNIDSchema.method('scrub', async function scrub() {
// * Remove all personal info from a PNID
// * Username and PID remain so thye do not get assigned again
Expand Down Expand Up @@ -287,6 +297,7 @@ PNIDSchema.method('scrub', async function scrub() {
});

this.deleted = true;
this.marked_for_deletion = false;
this.access_level = 0;
this.server_access_level = 'prod';
this.creation_date = '';
Expand Down
8 changes: 6 additions & 2 deletions src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import morgan from 'morgan';
import xmlbuilder from 'xmlbuilder';
import xmlparser from '@/middleware/xml-parser';
import { connect as connectCache } from '@/cache';
import { connect as connectDatabase } from '@/database';
import { checkMarkedDeletions, connect as connectDatabase } from '@/database';
import { startGRPCServer } from '@/services/grpc/server';
import { fullUrl, getValueFromHeaders } from '@/util';
import { fullUrl, getValueFromHeaders, setupScheduledTasks } from '@/util';
import { LOG_INFO, LOG_SUCCESS, LOG_WARN } from '@/logger';
import conntest from '@/services/conntest';
import cbvc from '@/services/cbvc';
Expand Down Expand Up @@ -116,6 +116,10 @@ async function main(): Promise<void> {

startProvisioner();

await checkMarkedDeletions();

setupScheduledTasks();

app.listen(config.http.port, () => {
LOG_SUCCESS(`HTTP server started on port ${config.http.port}`);
});
Expand Down
2 changes: 1 addition & 1 deletion src/services/api/routes/v1/login.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ router.post('/', async (request: express.Request, response: express.Response): P
}
}

if (pnid.deleted) {
if (pnid.deleted || pnid.marked_for_deletion) {
response.status(400).json({
app: 'api',
status: 400,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export async function exchangeTokenForUserData(request: ExchangeTokenForUserData
}

return {
deleted: pnid.deleted,
deleted: pnid.deleted || pnid.marked_for_deletion,
pid: pnid.pid,
username: pnid.username,
accessLevel: pnid.access_level,
Expand Down
2 changes: 1 addition & 1 deletion src/services/grpc/account/v1/get-user-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export async function getUserData(request: GetUserDataRequest): Promise<GetUserD
}

return {
deleted: pnid.deleted,
deleted: pnid.deleted || pnid.marked_for_deletion,
pid: pnid.pid,
username: pnid.username,
accessLevel: pnid.access_level,
Expand Down
4 changes: 2 additions & 2 deletions src/services/grpc/account/v2/delete-account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export async function deleteAccount(request: DeleteAccountRequest): Promise<Dele
try {
const email = pnid.email.address;

await pnid.scrub();
pnid.markForDeletion();
await pnid.save();

await sendPNIDDeletedEmail(email, pnid.username);
Expand All @@ -26,6 +26,6 @@ export async function deleteAccount(request: DeleteAccountRequest): Promise<Dele
}

return {
hasDeleted: pnid.deleted
hasDeleted: pnid.deleted || pnid.marked_for_deletion
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export async function exchangeTokenForUserData(request: ExchangeTokenForUserData
});

return {
deleted: pnid.deleted,
deleted: pnid.deleted || pnid.marked_for_deletion,
pid: pnid.pid,
username: pnid.username,
accessLevel: pnid.access_level,
Expand Down
2 changes: 1 addition & 1 deletion src/services/grpc/account/v2/get-user-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export async function getUserData(request: GetUserDataRequest): Promise<GetUserD
});

return {
deleted: pnid.deleted,
deleted: pnid.deleted || pnid.marked_for_deletion,
pid: pnid.pid,
username: pnid.username,
accessLevel: pnid.access_level,
Expand Down
2 changes: 1 addition & 1 deletion src/services/grpc/api/v1/get-user-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export async function getUserData(_request: Empty, context: CallContext & Authen
const pnid = context.pnid!;

return {
deleted: pnid.deleted,
deleted: pnid.deleted || pnid.marked_for_deletion,
creationDate: pnid.creation_date,
updatedDate: pnid.updated,
pid: pnid.pid,
Expand Down
2 changes: 1 addition & 1 deletion src/services/grpc/api/v1/login.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export async function login(request: LoginRequest): Promise<DeepPartial<LoginRes
}
}

if (pnid.deleted) {
if (pnid.deleted || pnid.marked_for_deletion) {
throw new ServerError(Status.UNAUTHENTICATED, 'Account has been deleted');
}

Expand Down
2 changes: 1 addition & 1 deletion src/services/grpc/api/v1/update-user-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export async function updateUserData(_request: UpdateUserDataRequest, context: C
// TODO - STUBBED, DO SOMETHING HERE

return {
deleted: pnid.deleted,
deleted: pnid.deleted || pnid.marked_for_deletion,
creationDate: pnid.creation_date,
updatedDate: pnid.updated,
pid: pnid.pid,
Expand Down
4 changes: 2 additions & 2 deletions src/services/grpc/api/v2/delete-account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export async function deleteAccount(request: DeleteAccountRequest): Promise<Dele
try {
const email = pnid.email.address;

await pnid.scrub();
pnid.markForDeletion();
await pnid.save();

await sendPNIDDeletedEmail(email, pnid.username);
Expand All @@ -26,6 +26,6 @@ export async function deleteAccount(request: DeleteAccountRequest): Promise<Dele
}

return {
hasDeleted: pnid.deleted
hasDeleted: pnid.deleted || pnid.marked_for_deletion
};
}
2 changes: 1 addition & 1 deletion src/services/grpc/api/v2/get-user-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export async function getUserData(_request: Empty, context: CallContext & Authen
const pnid = context.pnid!;

return {
deleted: pnid.deleted,
deleted: pnid.deleted || pnid.marked_for_deletion,
creationDate: pnid.creation_date,
updatedDate: pnid.updated,
pid: pnid.pid,
Expand Down
2 changes: 1 addition & 1 deletion src/services/grpc/api/v2/login.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export async function login(request: LoginRequest): Promise<DeepPartial<LoginRes
}
}

if (pnid.deleted) {
if (pnid.deleted || pnid.marked_for_deletion) {
throw new ServerError(Status.UNAUTHENTICATED, 'Account has been deleted');
}

Expand Down
2 changes: 1 addition & 1 deletion src/services/grpc/api/v2/update-user-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export async function updateUserData(_request: UpdateUserDataRequest, context: C
// TODO - STUBBED, DO SOMETHING HERE

return {
deleted: pnid.deleted,
deleted: pnid.deleted || pnid.marked_for_deletion,
creationDate: pnid.creation_date,
updatedDate: pnid.updated,
pid: pnid.pid,
Expand Down
2 changes: 1 addition & 1 deletion src/services/nnas/routes/oauth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ router.post('/access_token/generate', deviceCertificateMiddleware, consoleStatus
}
}

if (pnid.deleted) {
if (pnid.deleted || pnid.marked_for_deletion) {
// * 0112 is the "account deleted" error, but unsure if this unlinks the PNID from the user?
// * 0143 is the "The link to this Nintendo Network ID has been temporarliy removed" error,
// * maybe that is a better error to use here?
Expand Down
2 changes: 1 addition & 1 deletion src/services/nnas/routes/people.ts
Original file line number Diff line number Diff line change
Expand Up @@ -561,7 +561,7 @@ router.post('/@me/deletion', async (request: express.Request, response: express.

const email = pnid.email.address;

await pnid.scrub();
pnid.markForDeletion();
await pnid.save();

try {
Expand Down
3 changes: 3 additions & 0 deletions src/types/mongoose/pnid.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import type { PNIDPermissionFlag } from '@/types/common/permission-flags';

export interface IPNID {
deleted: boolean;
marked_for_deletion: boolean;
hard_delete_time: Date;
permissions: bigint;
access_level: number;
server_access_level: string;
Expand Down Expand Up @@ -81,6 +83,7 @@ export interface IPNIDMethods {
generateEmailValidationToken(): Promise<void>;
updateMii(mii: { name: string; primary: string; data: string }): Promise<void>;
generateMiiImages(): Promise<void>;
markForDeletion(): void;
scrub(): Promise<void>;
hasPermission(flag: PNIDPermissionFlag): boolean;
addPermission(flag: PNIDPermissionFlag): void;
Expand Down
Loading
Loading