POST /api/aliases/temporary
This commit is contained in:
parent
aa7c2b12eb
commit
54cefc8360
78
src/app/api/aliases/temporary/route.ts
Normal file
78
src/app/api/aliases/temporary/route.ts
Normal file
|
@ -0,0 +1,78 @@
|
|||
import apiAuth from "@/lib/apiAuth";
|
||||
import { auditLog } from "@/lib/audit";
|
||||
import { AliasEntry, createAliasEntry } from "@/lib/db";
|
||||
import { generateAliasEmail } from "@/lib/util";
|
||||
import { NextResponse } from "next/server";
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/aliases/temporary:
|
||||
* post:
|
||||
* description: Creates a new temporary alias
|
||||
* requestBody:
|
||||
* description: The options for the alias
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* label:
|
||||
* type: string
|
||||
* example: "reddit"
|
||||
* style:
|
||||
* type: string
|
||||
* enum: [words, random]
|
||||
* labelAtEnd:
|
||||
* type: boolean
|
||||
* security:
|
||||
* - api_key: []
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The created alias
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* id:
|
||||
* type: number
|
||||
* address:
|
||||
* type: string
|
||||
* example: "your@primary.email"
|
||||
* alias:
|
||||
* type: string
|
||||
* example: "reddit-whypark@t.amogus.cloud"
|
||||
* pending:
|
||||
* type: boolean
|
||||
* temporary:
|
||||
* type: boolean
|
||||
*/
|
||||
export async function POST(request: Request) {
|
||||
return await apiAuth(request, async (request, user) => {
|
||||
const body = await request.json();
|
||||
if (typeof body.label != "string") return new Response('"label" not provided', { status: 400 });
|
||||
if (!body.label.length || body.label.length > 16) return new Response('"label" must be between 1 and 16 characters long', { status: 400 });
|
||||
if (typeof body.style != "string") return new Response('"style" not provided', { status: 400 });
|
||||
if (typeof body.labelAtEnd != 'boolean') return new Response('"labelAtEnd" not provided', { status: 400 });
|
||||
|
||||
const email = await generateAliasEmail(body.label, body.style, body.labelAtEnd);
|
||||
const id = await createAliasEntry(
|
||||
user,
|
||||
email,
|
||||
false,
|
||||
true,
|
||||
);
|
||||
|
||||
const alias: AliasEntry = {
|
||||
id: id,
|
||||
address: user,
|
||||
alias: email,
|
||||
pending: false,
|
||||
temporary: true,
|
||||
};
|
||||
|
||||
auditLog("createTempAlias", alias);
|
||||
return NextResponse.json(alias);
|
||||
});
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
/**
|
||||
* @swagger
|
||||
* /api/test:
|
||||
* get:
|
||||
* description: Returns the hello world
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Hello World!
|
||||
*/
|
||||
export async function GET(_request: Request) {
|
||||
// Do whatever you want
|
||||
return new Response('Hello World!', {
|
||||
status: 200,
|
||||
});
|
||||
}
|
|
@ -3,11 +3,9 @@
|
|||
import crypto from "crypto";
|
||||
import fs from "fs/promises";
|
||||
import { getServerSession } from "next-auth";
|
||||
import * as random_words from "random-words";
|
||||
import { AuditLog, auditLog } from "./audit";
|
||||
import { AliasEntry, AliasRequestEntry, ApiKeyEntry, approveAliasEntry, createAliasEntry, createApiKeyEntry, createTempAliasRequestEntry, createUserEntry, database, deleteAliasEntry, deleteApiKey, deleteTempAliasRequestEntry, getAlias, getAllAliases, getApiKeyById, getTempAliasRequestEntry, getUserAliases, getUserApiKeys, setUserPassword } from "./db";
|
||||
import { aliasesNeedApproval, anonymizeApiKey, isAdmin } from "./util";
|
||||
import { TEMP_EMAIL_DOMAIN } from "./constants";
|
||||
import { aliasesNeedApproval, anonymizeApiKey, generateAliasEmail, isAdmin } from "./util";
|
||||
|
||||
export async function fetchAllUsers(): Promise<string[]> {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
|
@ -135,24 +133,7 @@ export async function requestTemporaryAlias(
|
|||
|
||||
if (!label.length || label.length > 16) throw new Error("Malformed request");
|
||||
|
||||
let email: string;
|
||||
do {
|
||||
let randomString: string;
|
||||
switch (style) {
|
||||
case 'words':
|
||||
randomString = random_words.generate(2).join('');
|
||||
break;
|
||||
case 'random':
|
||||
randomString = crypto
|
||||
.randomBytes(8)
|
||||
.toString('base64')
|
||||
.replace(/\W/, ''); // Delete special characters
|
||||
break;
|
||||
default:
|
||||
throw new Error("Invalid style");
|
||||
}
|
||||
email = `${labelAtEnd ? `${randomString}-${label}` : `${label}-${randomString}`}@${TEMP_EMAIL_DOMAIN}`;
|
||||
} while (!await aliasAvailable(email, true));
|
||||
let email = await generateAliasEmail(label, style, labelAtEnd);
|
||||
|
||||
const request: AliasRequestEntry = {
|
||||
key: crypto.randomBytes(12).toString('base64'),
|
||||
|
|
19
src/lib/apiAuth.ts
Normal file
19
src/lib/apiAuth.ts
Normal file
|
@ -0,0 +1,19 @@
|
|||
import { getApiKeyByToken } from "./db";
|
||||
|
||||
// Returns the user's email address or throws an error if unauthenticated
|
||||
export default async function apiAuth(request: Request, callback: (request: Request, user: string) => Response | Promise<Response>) {
|
||||
let authToken = request.headers.get("authorization");
|
||||
if (!authToken) return new Response("Unauthorized", { status: 401 });
|
||||
const token = authToken.replace(/^Bearer /, ""); // the annoying prefix should be optional
|
||||
|
||||
const key = await getApiKeyByToken(token);
|
||||
if (!key) return new Response("Unauthorized", { status: 401 });
|
||||
|
||||
const res = callback(request, key.address);;
|
||||
|
||||
if (res instanceof Promise) {
|
||||
return await res;
|
||||
} else {
|
||||
return res;
|
||||
}
|
||||
};
|
|
@ -11,10 +11,10 @@ export const getApiDocs = async () => {
|
|||
},
|
||||
components: {
|
||||
securitySchemes: {
|
||||
BearerAuth: {
|
||||
api_key: {
|
||||
type: 'http',
|
||||
scheme: 'bearer',
|
||||
bearerFormat: 'JWT',
|
||||
name: 'API key',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
import { Session } from 'next-auth';
|
||||
import crypto from 'crypto';
|
||||
import { ApiKeyEntry } from './db';
|
||||
import * as random_words from "random-words";
|
||||
import { TEMP_EMAIL_DOMAIN } from './constants';
|
||||
import { aliasAvailable } from './actions';
|
||||
|
||||
export function sha256sum(input: any) {
|
||||
const hash = crypto.createHash('sha256');
|
||||
|
@ -23,3 +26,26 @@ export function anonymizeApiKey(key: ApiKeyEntry): ApiKeyEntry {
|
|||
token: key.token.substring(0, 6) + "********",
|
||||
};
|
||||
}
|
||||
|
||||
export async function generateAliasEmail(label: string, style: 'words' | 'random', labelAtEnd: boolean): Promise<string> {
|
||||
let email: string;
|
||||
do {
|
||||
let randomString: string;
|
||||
switch (style) {
|
||||
case 'words':
|
||||
randomString = random_words.generate(2).join('');
|
||||
break;
|
||||
case 'random':
|
||||
randomString = crypto
|
||||
.randomBytes(8)
|
||||
.toString('base64')
|
||||
.replace(/\W/, ''); // Delete special characters
|
||||
break;
|
||||
default:
|
||||
throw new Error("Invalid style");
|
||||
}
|
||||
email = `${labelAtEnd ? `${randomString}-${label}` : `${label}-${randomString}`}@${TEMP_EMAIL_DOMAIN}`;
|
||||
} while (!await aliasAvailable(email, true));
|
||||
|
||||
return email;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue