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'
|
PORT='8000'
|
||||||
NODE_ENV='development'
|
NODE_ENV='development'
|
||||||
API_URL='http://localhost:8000'
|
API_URL='http://localhost:8000'
|
||||||
|
API_KEY='apiKeySecret'
|
||||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -35,3 +35,4 @@ npm-debug.log*
|
|||||||
|
|
||||||
# misc
|
# misc
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
/uploads
|
||||||
|
@ -12,7 +12,7 @@ const parameters = Type.Object({
|
|||||||
type Parameters = Static<typeof parameters>
|
type Parameters = Static<typeof parameters>
|
||||||
|
|
||||||
export const getServiceSchema: FastifySchema = {
|
export const getServiceSchema: FastifySchema = {
|
||||||
tags: ['uploads'] as string[],
|
tags: ['guilds'] as string[],
|
||||||
params: parameters,
|
params: parameters,
|
||||||
response: {
|
response: {
|
||||||
200: {
|
200: {
|
||||||
|
@ -8,15 +8,23 @@ import {
|
|||||||
MAXIMUM_IMAGE_SIZE,
|
MAXIMUM_IMAGE_SIZE,
|
||||||
SUPPORTED_IMAGE_MIMETYPE
|
SUPPORTED_IMAGE_MIMETYPE
|
||||||
} from '../../../tools/configurations/index.js'
|
} from '../../../tools/configurations/index.js'
|
||||||
|
import verifyAPIKey from '../../../tools/plugins/verifyAPIKey.js'
|
||||||
|
|
||||||
const postServiceSchema: FastifySchema = {
|
const postServiceSchema: FastifySchema = {
|
||||||
description: 'Uploads guild icon',
|
description: 'Uploads guild icon',
|
||||||
tags: ['uploads'] as string[],
|
tags: ['guilds'] as string[],
|
||||||
|
security: [
|
||||||
|
{
|
||||||
|
apiKeyAuth: []
|
||||||
|
}
|
||||||
|
] as Array<{ [key: string]: [] }>,
|
||||||
consumes: ['multipart/form-data'] as string[],
|
consumes: ['multipart/form-data'] as string[],
|
||||||
produces: ['application/json'] as string[],
|
produces: ['application/json'] as string[],
|
||||||
response: {
|
response: {
|
||||||
201: Type.String(),
|
201: Type.String(),
|
||||||
400: fastifyErrors[400],
|
400: fastifyErrors[400],
|
||||||
|
401: fastifyErrors[401],
|
||||||
|
403: fastifyErrors[403],
|
||||||
431: fastifyErrors[431],
|
431: fastifyErrors[431],
|
||||||
500: fastifyErrors[500]
|
500: fastifyErrors[500]
|
||||||
}
|
}
|
||||||
@ -26,12 +34,16 @@ export const postGuildsUploadsIconService: FastifyPluginAsync = async (
|
|||||||
fastify
|
fastify
|
||||||
) => {
|
) => {
|
||||||
await fastify.register(fastifyMultipart)
|
await fastify.register(fastifyMultipart)
|
||||||
|
await fastify.register(verifyAPIKey)
|
||||||
|
|
||||||
fastify.route({
|
fastify.route({
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
url: '/uploads/guilds',
|
url: '/uploads/guilds',
|
||||||
schema: postServiceSchema,
|
schema: postServiceSchema,
|
||||||
handler: async (request, reply) => {
|
handler: async (request, reply) => {
|
||||||
|
if (request.apiKey == null) {
|
||||||
|
throw fastify.httpErrors.forbidden()
|
||||||
|
}
|
||||||
const file = await uploadFile({
|
const file = await uploadFile({
|
||||||
fastify,
|
fastify,
|
||||||
request,
|
request,
|
||||||
|
@ -12,7 +12,7 @@ const parameters = Type.Object({
|
|||||||
type Parameters = Static<typeof parameters>
|
type Parameters = Static<typeof parameters>
|
||||||
|
|
||||||
export const getServiceSchema: FastifySchema = {
|
export const getServiceSchema: FastifySchema = {
|
||||||
tags: ['uploads'] as string[],
|
tags: ['messages'] as string[],
|
||||||
params: parameters,
|
params: parameters,
|
||||||
response: {
|
response: {
|
||||||
200: {
|
200: {
|
||||||
|
@ -5,15 +5,23 @@ import fastifyMultipart from 'fastify-multipart'
|
|||||||
import { fastifyErrors } from '../../../models/utils.js'
|
import { fastifyErrors } from '../../../models/utils.js'
|
||||||
import { uploadFile } from '../../../tools/utils/uploadFile.js'
|
import { uploadFile } from '../../../tools/utils/uploadFile.js'
|
||||||
import { MAXIMUM_IMAGE_SIZE } from '../../../tools/configurations/index.js'
|
import { MAXIMUM_IMAGE_SIZE } from '../../../tools/configurations/index.js'
|
||||||
|
import verifyAPIKey from '../../../tools/plugins/verifyAPIKey.js'
|
||||||
|
|
||||||
const postServiceSchema: FastifySchema = {
|
const postServiceSchema: FastifySchema = {
|
||||||
description: 'Uploads message file',
|
description: 'Uploads message file',
|
||||||
tags: ['uploads'] as string[],
|
tags: ['messages'] as string[],
|
||||||
|
security: [
|
||||||
|
{
|
||||||
|
apiKeyAuth: []
|
||||||
|
}
|
||||||
|
] as Array<{ [key: string]: [] }>,
|
||||||
consumes: ['multipart/form-data'] as string[],
|
consumes: ['multipart/form-data'] as string[],
|
||||||
produces: ['application/json'] as string[],
|
produces: ['application/json'] as string[],
|
||||||
response: {
|
response: {
|
||||||
201: Type.String(),
|
201: Type.String(),
|
||||||
400: fastifyErrors[400],
|
400: fastifyErrors[400],
|
||||||
|
401: fastifyErrors[401],
|
||||||
|
403: fastifyErrors[403],
|
||||||
431: fastifyErrors[431],
|
431: fastifyErrors[431],
|
||||||
500: fastifyErrors[500]
|
500: fastifyErrors[500]
|
||||||
}
|
}
|
||||||
@ -23,12 +31,16 @@ export const postMessagesUploadsService: FastifyPluginAsync = async (
|
|||||||
fastify
|
fastify
|
||||||
) => {
|
) => {
|
||||||
await fastify.register(fastifyMultipart)
|
await fastify.register(fastifyMultipart)
|
||||||
|
await fastify.register(verifyAPIKey)
|
||||||
|
|
||||||
fastify.route({
|
fastify.route({
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
url: '/uploads/messages',
|
url: '/uploads/messages',
|
||||||
schema: postServiceSchema,
|
schema: postServiceSchema,
|
||||||
handler: async (request, reply) => {
|
handler: async (request, reply) => {
|
||||||
|
if (request.apiKey == null) {
|
||||||
|
throw fastify.httpErrors.forbidden()
|
||||||
|
}
|
||||||
const file = await uploadFile({
|
const file = await uploadFile({
|
||||||
fastify,
|
fastify,
|
||||||
request,
|
request,
|
||||||
|
@ -12,7 +12,7 @@ const parameters = Type.Object({
|
|||||||
type Parameters = Static<typeof parameters>
|
type Parameters = Static<typeof parameters>
|
||||||
|
|
||||||
export const getServiceSchema: FastifySchema = {
|
export const getServiceSchema: FastifySchema = {
|
||||||
tags: ['uploads'] as string[],
|
tags: ['users'] as string[],
|
||||||
params: parameters,
|
params: parameters,
|
||||||
response: {
|
response: {
|
||||||
200: {
|
200: {
|
||||||
|
@ -8,15 +8,23 @@ import {
|
|||||||
MAXIMUM_IMAGE_SIZE,
|
MAXIMUM_IMAGE_SIZE,
|
||||||
SUPPORTED_IMAGE_MIMETYPE
|
SUPPORTED_IMAGE_MIMETYPE
|
||||||
} from '../../../tools/configurations/index.js'
|
} from '../../../tools/configurations/index.js'
|
||||||
|
import verifyAPIKey from '../../../tools/plugins/verifyAPIKey.js'
|
||||||
|
|
||||||
const postServiceSchema: FastifySchema = {
|
const postServiceSchema: FastifySchema = {
|
||||||
description: 'Uploads user logo',
|
description: 'Uploads user logo',
|
||||||
tags: ['uploads'] as string[],
|
tags: ['users'] as string[],
|
||||||
|
security: [
|
||||||
|
{
|
||||||
|
apiKeyAuth: []
|
||||||
|
}
|
||||||
|
] as Array<{ [key: string]: [] }>,
|
||||||
consumes: ['multipart/form-data'] as string[],
|
consumes: ['multipart/form-data'] as string[],
|
||||||
produces: ['application/json'] as string[],
|
produces: ['application/json'] as string[],
|
||||||
response: {
|
response: {
|
||||||
201: Type.String(),
|
201: Type.String(),
|
||||||
400: fastifyErrors[400],
|
400: fastifyErrors[400],
|
||||||
|
401: fastifyErrors[401],
|
||||||
|
403: fastifyErrors[403],
|
||||||
431: fastifyErrors[431],
|
431: fastifyErrors[431],
|
||||||
500: fastifyErrors[500]
|
500: fastifyErrors[500]
|
||||||
}
|
}
|
||||||
@ -26,12 +34,16 @@ export const postUsersUploadsLogoService: FastifyPluginAsync = async (
|
|||||||
fastify
|
fastify
|
||||||
) => {
|
) => {
|
||||||
await fastify.register(fastifyMultipart)
|
await fastify.register(fastifyMultipart)
|
||||||
|
await fastify.register(verifyAPIKey)
|
||||||
|
|
||||||
fastify.route({
|
fastify.route({
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
url: '/uploads/users',
|
url: '/uploads/users',
|
||||||
schema: postServiceSchema,
|
schema: postServiceSchema,
|
||||||
handler: async (request, reply) => {
|
handler: async (request, reply) => {
|
||||||
|
if (request.apiKey == null) {
|
||||||
|
throw fastify.httpErrors.forbidden()
|
||||||
|
}
|
||||||
const file = await uploadFile({
|
const file = await uploadFile({
|
||||||
fastify,
|
fastify,
|
||||||
request,
|
request,
|
||||||
|
@ -7,6 +7,7 @@ dotenv.config()
|
|||||||
export const PORT = parseInt(process.env.PORT ?? '8000', 10)
|
export const PORT = parseInt(process.env.PORT ?? '8000', 10)
|
||||||
export const HOST = process.env.HOST ?? '0.0.0.0'
|
export const HOST = process.env.HOST ?? '0.0.0.0'
|
||||||
export const API_URL = process.env.API_URL ?? `http://${HOST}:${PORT}`
|
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 SRC_URL = new URL('../../', import.meta.url)
|
||||||
export const ROOT_URL = new URL('../', SRC_URL)
|
export const ROOT_URL = new URL('../', SRC_URL)
|
||||||
|
@ -14,7 +14,16 @@ export const swaggerOptions: FastifyDynamicSwaggerOptions = {
|
|||||||
description: packageJSON.description,
|
description: packageJSON.description,
|
||||||
version: packageJSON.version
|
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,
|
exposeRoute: true,
|
||||||
staticCSP: 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