feat: add API Key security for POST uploads
This commit is contained in:
parent
7d8b88d5d2
commit
ddab264c7f
@ -2,3 +2,4 @@ HOST='0.0.0.0'
|
||||
PORT='8000'
|
||||
NODE_ENV='development'
|
||||
API_URL='http://localhost:8000'
|
||||
API_KEY='apiKeySecret'
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -35,3 +35,4 @@ npm-debug.log*
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
/uploads
|
||||
|
@ -12,7 +12,7 @@ const parameters = Type.Object({
|
||||
type Parameters = Static<typeof parameters>
|
||||
|
||||
export const getServiceSchema: FastifySchema = {
|
||||
tags: ['uploads'] as string[],
|
||||
tags: ['guilds'] as string[],
|
||||
params: parameters,
|
||||
response: {
|
||||
200: {
|
||||
|
@ -8,15 +8,23 @@ import {
|
||||
MAXIMUM_IMAGE_SIZE,
|
||||
SUPPORTED_IMAGE_MIMETYPE
|
||||
} from '../../../tools/configurations/index.js'
|
||||
import verifyAPIKey from '../../../tools/plugins/verifyAPIKey.js'
|
||||
|
||||
const postServiceSchema: FastifySchema = {
|
||||
description: 'Uploads guild icon',
|
||||
tags: ['uploads'] as string[],
|
||||
tags: ['guilds'] as string[],
|
||||
security: [
|
||||
{
|
||||
apiKeyAuth: []
|
||||
}
|
||||
] as Array<{ [key: string]: [] }>,
|
||||
consumes: ['multipart/form-data'] as string[],
|
||||
produces: ['application/json'] as string[],
|
||||
response: {
|
||||
201: Type.String(),
|
||||
400: fastifyErrors[400],
|
||||
401: fastifyErrors[401],
|
||||
403: fastifyErrors[403],
|
||||
431: fastifyErrors[431],
|
||||
500: fastifyErrors[500]
|
||||
}
|
||||
@ -26,12 +34,16 @@ export const postGuildsUploadsIconService: FastifyPluginAsync = async (
|
||||
fastify
|
||||
) => {
|
||||
await fastify.register(fastifyMultipart)
|
||||
await fastify.register(verifyAPIKey)
|
||||
|
||||
fastify.route({
|
||||
method: 'POST',
|
||||
url: '/uploads/guilds',
|
||||
schema: postServiceSchema,
|
||||
handler: async (request, reply) => {
|
||||
if (request.apiKey == null) {
|
||||
throw fastify.httpErrors.forbidden()
|
||||
}
|
||||
const file = await uploadFile({
|
||||
fastify,
|
||||
request,
|
||||
|
@ -12,7 +12,7 @@ const parameters = Type.Object({
|
||||
type Parameters = Static<typeof parameters>
|
||||
|
||||
export const getServiceSchema: FastifySchema = {
|
||||
tags: ['uploads'] as string[],
|
||||
tags: ['messages'] as string[],
|
||||
params: parameters,
|
||||
response: {
|
||||
200: {
|
||||
|
@ -5,15 +5,23 @@ import fastifyMultipart from 'fastify-multipart'
|
||||
import { fastifyErrors } from '../../../models/utils.js'
|
||||
import { uploadFile } from '../../../tools/utils/uploadFile.js'
|
||||
import { MAXIMUM_IMAGE_SIZE } from '../../../tools/configurations/index.js'
|
||||
import verifyAPIKey from '../../../tools/plugins/verifyAPIKey.js'
|
||||
|
||||
const postServiceSchema: FastifySchema = {
|
||||
description: 'Uploads message file',
|
||||
tags: ['uploads'] as string[],
|
||||
tags: ['messages'] as string[],
|
||||
security: [
|
||||
{
|
||||
apiKeyAuth: []
|
||||
}
|
||||
] as Array<{ [key: string]: [] }>,
|
||||
consumes: ['multipart/form-data'] as string[],
|
||||
produces: ['application/json'] as string[],
|
||||
response: {
|
||||
201: Type.String(),
|
||||
400: fastifyErrors[400],
|
||||
401: fastifyErrors[401],
|
||||
403: fastifyErrors[403],
|
||||
431: fastifyErrors[431],
|
||||
500: fastifyErrors[500]
|
||||
}
|
||||
@ -23,12 +31,16 @@ export const postMessagesUploadsService: FastifyPluginAsync = async (
|
||||
fastify
|
||||
) => {
|
||||
await fastify.register(fastifyMultipart)
|
||||
await fastify.register(verifyAPIKey)
|
||||
|
||||
fastify.route({
|
||||
method: 'POST',
|
||||
url: '/uploads/messages',
|
||||
schema: postServiceSchema,
|
||||
handler: async (request, reply) => {
|
||||
if (request.apiKey == null) {
|
||||
throw fastify.httpErrors.forbidden()
|
||||
}
|
||||
const file = await uploadFile({
|
||||
fastify,
|
||||
request,
|
||||
|
@ -12,7 +12,7 @@ const parameters = Type.Object({
|
||||
type Parameters = Static<typeof parameters>
|
||||
|
||||
export const getServiceSchema: FastifySchema = {
|
||||
tags: ['uploads'] as string[],
|
||||
tags: ['users'] as string[],
|
||||
params: parameters,
|
||||
response: {
|
||||
200: {
|
||||
|
@ -8,15 +8,23 @@ import {
|
||||
MAXIMUM_IMAGE_SIZE,
|
||||
SUPPORTED_IMAGE_MIMETYPE
|
||||
} from '../../../tools/configurations/index.js'
|
||||
import verifyAPIKey from '../../../tools/plugins/verifyAPIKey.js'
|
||||
|
||||
const postServiceSchema: FastifySchema = {
|
||||
description: 'Uploads user logo',
|
||||
tags: ['uploads'] as string[],
|
||||
tags: ['users'] as string[],
|
||||
security: [
|
||||
{
|
||||
apiKeyAuth: []
|
||||
}
|
||||
] as Array<{ [key: string]: [] }>,
|
||||
consumes: ['multipart/form-data'] as string[],
|
||||
produces: ['application/json'] as string[],
|
||||
response: {
|
||||
201: Type.String(),
|
||||
400: fastifyErrors[400],
|
||||
401: fastifyErrors[401],
|
||||
403: fastifyErrors[403],
|
||||
431: fastifyErrors[431],
|
||||
500: fastifyErrors[500]
|
||||
}
|
||||
@ -26,12 +34,16 @@ export const postUsersUploadsLogoService: FastifyPluginAsync = async (
|
||||
fastify
|
||||
) => {
|
||||
await fastify.register(fastifyMultipart)
|
||||
await fastify.register(verifyAPIKey)
|
||||
|
||||
fastify.route({
|
||||
method: 'POST',
|
||||
url: '/uploads/users',
|
||||
schema: postServiceSchema,
|
||||
handler: async (request, reply) => {
|
||||
if (request.apiKey == null) {
|
||||
throw fastify.httpErrors.forbidden()
|
||||
}
|
||||
const file = await uploadFile({
|
||||
fastify,
|
||||
request,
|
||||
|
@ -7,6 +7,7 @@ dotenv.config()
|
||||
export const PORT = parseInt(process.env.PORT ?? '8000', 10)
|
||||
export const HOST = process.env.HOST ?? '0.0.0.0'
|
||||
export const API_URL = process.env.API_URL ?? `http://${HOST}:${PORT}`
|
||||
export const API_KEY = process.env.API_KEY ?? 'apiKeySecret'
|
||||
|
||||
export const SRC_URL = new URL('../../', import.meta.url)
|
||||
export const ROOT_URL = new URL('../', SRC_URL)
|
||||
|
@ -14,7 +14,16 @@ export const swaggerOptions: FastifyDynamicSwaggerOptions = {
|
||||
description: packageJSON.description,
|
||||
version: packageJSON.version
|
||||
},
|
||||
tags: [{ name: 'guilds' }, { name: 'messages' }, { name: 'users' }]
|
||||
tags: [{ name: 'guilds' }, { name: 'messages' }, { name: 'users' }],
|
||||
components: {
|
||||
securitySchemes: {
|
||||
apiKeyAuth: {
|
||||
type: 'apiKey',
|
||||
name: 'X-API-KEY',
|
||||
in: 'header'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
exposeRoute: true,
|
||||
staticCSP: true,
|
||||
|
29
src/tools/plugins/verifyAPIKey.ts
Normal file
29
src/tools/plugins/verifyAPIKey.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import fastifyPlugin from 'fastify-plugin'
|
||||
import httpErrors from 'http-errors'
|
||||
|
||||
import { API_KEY } from '../configurations/index.js'
|
||||
|
||||
const { Unauthorized, Forbidden } = httpErrors
|
||||
|
||||
declare module 'fastify' {
|
||||
export interface FastifyRequest {
|
||||
apiKey?: string
|
||||
}
|
||||
}
|
||||
|
||||
export default fastifyPlugin(
|
||||
async (fastify) => {
|
||||
await fastify.decorateRequest('apiKey', null)
|
||||
await fastify.addHook('onRequest', async (request) => {
|
||||
const apiKey = request.headers['x-api-key']
|
||||
if (apiKey == null || typeof apiKey !== 'string') {
|
||||
throw new Unauthorized()
|
||||
}
|
||||
if (apiKey !== API_KEY) {
|
||||
throw new Forbidden()
|
||||
}
|
||||
request.apiKey = apiKey
|
||||
})
|
||||
},
|
||||
{ fastify: '3.x' }
|
||||
)
|
Reference in New Issue
Block a user