Fix runner api rate limit bypass
This commit is contained in:
parent
923e41fa4f
commit
e915cde30e
|
@ -16,6 +16,7 @@ import {
|
||||||
abusesSortValidator,
|
abusesSortValidator,
|
||||||
abuseUpdateValidator,
|
abuseUpdateValidator,
|
||||||
addAbuseMessageValidator,
|
addAbuseMessageValidator,
|
||||||
|
apiRateLimiter,
|
||||||
asyncMiddleware,
|
asyncMiddleware,
|
||||||
asyncRetryTransactionMiddleware,
|
asyncRetryTransactionMiddleware,
|
||||||
authenticate,
|
authenticate,
|
||||||
|
@ -32,6 +33,8 @@ import { AccountModel } from '../../models/account/account'
|
||||||
|
|
||||||
const abuseRouter = express.Router()
|
const abuseRouter = express.Router()
|
||||||
|
|
||||||
|
abuseRouter.use(apiRateLimiter)
|
||||||
|
|
||||||
abuseRouter.get('/',
|
abuseRouter.get('/',
|
||||||
openapiOperationDoc({ operationId: 'getAbuses' }),
|
openapiOperationDoc({ operationId: 'getAbuses' }),
|
||||||
authenticate,
|
authenticate,
|
||||||
|
|
|
@ -9,6 +9,7 @@ import { getFormattedObjects } from '../../helpers/utils'
|
||||||
import { JobQueue } from '../../lib/job-queue'
|
import { JobQueue } from '../../lib/job-queue'
|
||||||
import { Hooks } from '../../lib/plugins/hooks'
|
import { Hooks } from '../../lib/plugins/hooks'
|
||||||
import {
|
import {
|
||||||
|
apiRateLimiter,
|
||||||
asyncMiddleware,
|
asyncMiddleware,
|
||||||
authenticate,
|
authenticate,
|
||||||
commonVideosFiltersValidator,
|
commonVideosFiltersValidator,
|
||||||
|
@ -41,6 +42,8 @@ import { VideoPlaylistModel } from '../../models/video/video-playlist'
|
||||||
|
|
||||||
const accountsRouter = express.Router()
|
const accountsRouter = express.Router()
|
||||||
|
|
||||||
|
accountsRouter.use(apiRateLimiter)
|
||||||
|
|
||||||
accountsRouter.get('/',
|
accountsRouter.get('/',
|
||||||
paginationValidator,
|
paginationValidator,
|
||||||
accountsSortValidator,
|
accountsSortValidator,
|
||||||
|
|
|
@ -1,15 +1,17 @@
|
||||||
import express from 'express'
|
import express from 'express'
|
||||||
import { handleToNameAndHost } from '@server/helpers/actors'
|
import { handleToNameAndHost } from '@server/helpers/actors'
|
||||||
|
import { logger } from '@server/helpers/logger'
|
||||||
import { AccountBlocklistModel } from '@server/models/account/account-blocklist'
|
import { AccountBlocklistModel } from '@server/models/account/account-blocklist'
|
||||||
import { getServerActor } from '@server/models/application/application'
|
import { getServerActor } from '@server/models/application/application'
|
||||||
import { ServerBlocklistModel } from '@server/models/server/server-blocklist'
|
import { ServerBlocklistModel } from '@server/models/server/server-blocklist'
|
||||||
import { MActorAccountId, MUserAccountId } from '@server/types/models'
|
import { MActorAccountId, MUserAccountId } from '@server/types/models'
|
||||||
import { BlockStatus } from '@shared/models'
|
import { BlockStatus } from '@shared/models'
|
||||||
import { asyncMiddleware, blocklistStatusValidator, optionalAuthenticate } from '../../middlewares'
|
import { apiRateLimiter, asyncMiddleware, blocklistStatusValidator, optionalAuthenticate } from '../../middlewares'
|
||||||
import { logger } from '@server/helpers/logger'
|
|
||||||
|
|
||||||
const blocklistRouter = express.Router()
|
const blocklistRouter = express.Router()
|
||||||
|
|
||||||
|
blocklistRouter.use(apiRateLimiter)
|
||||||
|
|
||||||
blocklistRouter.get('/status',
|
blocklistRouter.get('/status',
|
||||||
optionalAuthenticate,
|
optionalAuthenticate,
|
||||||
blocklistStatusValidator,
|
blocklistStatusValidator,
|
||||||
|
|
|
@ -4,10 +4,12 @@ import { bulkRemoveCommentsOfValidator } from '@server/middlewares/validators/bu
|
||||||
import { VideoCommentModel } from '@server/models/video/video-comment'
|
import { VideoCommentModel } from '@server/models/video/video-comment'
|
||||||
import { HttpStatusCode } from '@shared/models'
|
import { HttpStatusCode } from '@shared/models'
|
||||||
import { BulkRemoveCommentsOfBody } from '@shared/models/bulk/bulk-remove-comments-of-body.model'
|
import { BulkRemoveCommentsOfBody } from '@shared/models/bulk/bulk-remove-comments-of-body.model'
|
||||||
import { asyncMiddleware, authenticate } from '../../middlewares'
|
import { apiRateLimiter, asyncMiddleware, authenticate } from '../../middlewares'
|
||||||
|
|
||||||
const bulkRouter = express.Router()
|
const bulkRouter = express.Router()
|
||||||
|
|
||||||
|
bulkRouter.use(apiRateLimiter)
|
||||||
|
|
||||||
bulkRouter.post('/remove-comments-of',
|
bulkRouter.post('/remove-comments-of',
|
||||||
authenticate,
|
authenticate,
|
||||||
asyncMiddleware(bulkRemoveCommentsOfValidator),
|
asyncMiddleware(bulkRemoveCommentsOfValidator),
|
||||||
|
|
|
@ -8,11 +8,13 @@ import { auditLoggerFactory, CustomConfigAuditView, getAuditIdFromRes } from '..
|
||||||
import { objectConverter } from '../../helpers/core-utils'
|
import { objectConverter } from '../../helpers/core-utils'
|
||||||
import { CONFIG, reloadConfig } from '../../initializers/config'
|
import { CONFIG, reloadConfig } from '../../initializers/config'
|
||||||
import { ClientHtml } from '../../lib/client-html'
|
import { ClientHtml } from '../../lib/client-html'
|
||||||
import { asyncMiddleware, authenticate, ensureUserHasRight, openapiOperationDoc } from '../../middlewares'
|
import { apiRateLimiter, asyncMiddleware, authenticate, ensureUserHasRight, openapiOperationDoc } from '../../middlewares'
|
||||||
import { customConfigUpdateValidator, ensureConfigIsEditable } from '../../middlewares/validators/config'
|
import { customConfigUpdateValidator, ensureConfigIsEditable } from '../../middlewares/validators/config'
|
||||||
|
|
||||||
const configRouter = express.Router()
|
const configRouter = express.Router()
|
||||||
|
|
||||||
|
configRouter.use(apiRateLimiter)
|
||||||
|
|
||||||
const auditLogger = auditLoggerFactory('config')
|
const auditLogger = auditLoggerFactory('config')
|
||||||
|
|
||||||
configRouter.get('/',
|
configRouter.get('/',
|
||||||
|
|
|
@ -2,10 +2,12 @@ import express from 'express'
|
||||||
import { ServerConfigManager } from '@server/lib/server-config-manager'
|
import { ServerConfigManager } from '@server/lib/server-config-manager'
|
||||||
import { ActorCustomPageModel } from '@server/models/account/actor-custom-page'
|
import { ActorCustomPageModel } from '@server/models/account/actor-custom-page'
|
||||||
import { HttpStatusCode, UserRight } from '@shared/models'
|
import { HttpStatusCode, UserRight } from '@shared/models'
|
||||||
import { asyncMiddleware, authenticate, ensureUserHasRight } from '../../middlewares'
|
import { apiRateLimiter, asyncMiddleware, authenticate, ensureUserHasRight } from '../../middlewares'
|
||||||
|
|
||||||
const customPageRouter = express.Router()
|
const customPageRouter = express.Router()
|
||||||
|
|
||||||
|
customPageRouter.use(apiRateLimiter)
|
||||||
|
|
||||||
customPageRouter.get('/homepage/instance',
|
customPageRouter.get('/homepage/instance',
|
||||||
asyncMiddleware(getInstanceHomepage)
|
asyncMiddleware(getInstanceHomepage)
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
import cors from 'cors'
|
import cors from 'cors'
|
||||||
import express from 'express'
|
import express from 'express'
|
||||||
import { buildRateLimiter } from '@server/middlewares'
|
|
||||||
import { HttpStatusCode } from '../../../shared/models'
|
import { HttpStatusCode } from '../../../shared/models'
|
||||||
import { badRequest } from '../../helpers/express-utils'
|
import { badRequest } from '../../helpers/express-utils'
|
||||||
import { CONFIG } from '../../initializers/config'
|
|
||||||
import { abuseRouter } from './abuse'
|
import { abuseRouter } from './abuse'
|
||||||
import { accountsRouter } from './accounts'
|
import { accountsRouter } from './accounts'
|
||||||
import { blocklistRouter } from './blocklist'
|
import { blocklistRouter } from './blocklist'
|
||||||
|
@ -32,12 +31,6 @@ apiRouter.use(cors({
|
||||||
credentials: true
|
credentials: true
|
||||||
}))
|
}))
|
||||||
|
|
||||||
const apiRateLimiter = buildRateLimiter({
|
|
||||||
windowMs: CONFIG.RATES_LIMIT.API.WINDOW_MS,
|
|
||||||
max: CONFIG.RATES_LIMIT.API.MAX
|
|
||||||
})
|
|
||||||
apiRouter.use(apiRateLimiter)
|
|
||||||
|
|
||||||
apiRouter.use('/server', serverRouter)
|
apiRouter.use('/server', serverRouter)
|
||||||
apiRouter.use('/abuses', abuseRouter)
|
apiRouter.use('/abuses', abuseRouter)
|
||||||
apiRouter.use('/bulk', bulkRouter)
|
apiRouter.use('/bulk', bulkRouter)
|
||||||
|
@ -57,6 +50,8 @@ apiRouter.use('/plugins', pluginRouter)
|
||||||
apiRouter.use('/custom-pages', customPageRouter)
|
apiRouter.use('/custom-pages', customPageRouter)
|
||||||
apiRouter.use('/blocklist', blocklistRouter)
|
apiRouter.use('/blocklist', blocklistRouter)
|
||||||
apiRouter.use('/runners', runnersRouter)
|
apiRouter.use('/runners', runnersRouter)
|
||||||
|
|
||||||
|
// apiRouter.use(apiRateLimiter)
|
||||||
apiRouter.use('/ping', pong)
|
apiRouter.use('/ping', pong)
|
||||||
apiRouter.use('/*', badRequest)
|
apiRouter.use('/*', badRequest)
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { HttpStatusCode, Job, JobState, JobType, ResultList, UserRight } from '@
|
||||||
import { isArray } from '../../helpers/custom-validators/misc'
|
import { isArray } from '../../helpers/custom-validators/misc'
|
||||||
import { JobQueue } from '../../lib/job-queue'
|
import { JobQueue } from '../../lib/job-queue'
|
||||||
import {
|
import {
|
||||||
|
apiRateLimiter,
|
||||||
asyncMiddleware,
|
asyncMiddleware,
|
||||||
authenticate,
|
authenticate,
|
||||||
ensureUserHasRight,
|
ensureUserHasRight,
|
||||||
|
@ -17,6 +18,8 @@ import { listJobsValidator } from '../../middlewares/validators/jobs'
|
||||||
|
|
||||||
const jobsRouter = express.Router()
|
const jobsRouter = express.Router()
|
||||||
|
|
||||||
|
jobsRouter.use(apiRateLimiter)
|
||||||
|
|
||||||
jobsRouter.post('/pause',
|
jobsRouter.post('/pause',
|
||||||
authenticate,
|
authenticate,
|
||||||
ensureUserHasRight(UserRight.MANAGE_JOBS),
|
ensureUserHasRight(UserRight.MANAGE_JOBS),
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
import express from 'express'
|
import express from 'express'
|
||||||
|
import { CONFIG } from '@server/initializers/config'
|
||||||
import { OpenTelemetryMetrics } from '@server/lib/opentelemetry/metrics'
|
import { OpenTelemetryMetrics } from '@server/lib/opentelemetry/metrics'
|
||||||
import { HttpStatusCode, PlaybackMetricCreate } from '@shared/models'
|
import { HttpStatusCode, PlaybackMetricCreate } from '@shared/models'
|
||||||
import { addPlaybackMetricValidator, asyncMiddleware } from '../../middlewares'
|
import { addPlaybackMetricValidator, apiRateLimiter, asyncMiddleware } from '../../middlewares'
|
||||||
import { CONFIG } from '@server/initializers/config'
|
|
||||||
|
|
||||||
const metricsRouter = express.Router()
|
const metricsRouter = express.Router()
|
||||||
|
|
||||||
|
metricsRouter.use(apiRateLimiter)
|
||||||
|
|
||||||
metricsRouter.post('/playback',
|
metricsRouter.post('/playback',
|
||||||
asyncMiddleware(addPlaybackMetricValidator),
|
asyncMiddleware(addPlaybackMetricValidator),
|
||||||
addPlaybackMetric
|
addPlaybackMetric
|
||||||
|
|
|
@ -4,10 +4,12 @@ import { OAuthClientModel } from '@server/models/oauth/oauth-client'
|
||||||
import { HttpStatusCode, OAuthClientLocal } from '@shared/models'
|
import { HttpStatusCode, OAuthClientLocal } from '@shared/models'
|
||||||
import { logger } from '../../helpers/logger'
|
import { logger } from '../../helpers/logger'
|
||||||
import { CONFIG } from '../../initializers/config'
|
import { CONFIG } from '../../initializers/config'
|
||||||
import { asyncMiddleware, openapiOperationDoc } from '../../middlewares'
|
import { apiRateLimiter, asyncMiddleware, openapiOperationDoc } from '../../middlewares'
|
||||||
|
|
||||||
const oauthClientsRouter = express.Router()
|
const oauthClientsRouter = express.Router()
|
||||||
|
|
||||||
|
oauthClientsRouter.use(apiRateLimiter)
|
||||||
|
|
||||||
oauthClientsRouter.get('/local',
|
oauthClientsRouter.get('/local',
|
||||||
openapiOperationDoc({ operationId: 'getOAuthClient' }),
|
openapiOperationDoc({ operationId: 'getOAuthClient' }),
|
||||||
asyncMiddleware(getLocalClient)
|
asyncMiddleware(getLocalClient)
|
||||||
|
|
|
@ -2,16 +2,18 @@ import express from 'express'
|
||||||
import memoizee from 'memoizee'
|
import memoizee from 'memoizee'
|
||||||
import { logger } from '@server/helpers/logger'
|
import { logger } from '@server/helpers/logger'
|
||||||
import { Hooks } from '@server/lib/plugins/hooks'
|
import { Hooks } from '@server/lib/plugins/hooks'
|
||||||
|
import { getServerActor } from '@server/models/application/application'
|
||||||
import { VideoModel } from '@server/models/video/video'
|
import { VideoModel } from '@server/models/video/video'
|
||||||
import { CategoryOverview, ChannelOverview, TagOverview, VideosOverview } from '../../../shared/models/overviews'
|
import { CategoryOverview, ChannelOverview, TagOverview, VideosOverview } from '../../../shared/models/overviews'
|
||||||
import { buildNSFWFilter } from '../../helpers/express-utils'
|
import { buildNSFWFilter } from '../../helpers/express-utils'
|
||||||
import { MEMOIZE_TTL, OVERVIEWS } from '../../initializers/constants'
|
import { MEMOIZE_TTL, OVERVIEWS } from '../../initializers/constants'
|
||||||
import { asyncMiddleware, optionalAuthenticate, videosOverviewValidator } from '../../middlewares'
|
import { apiRateLimiter, asyncMiddleware, optionalAuthenticate, videosOverviewValidator } from '../../middlewares'
|
||||||
import { TagModel } from '../../models/video/tag'
|
import { TagModel } from '../../models/video/tag'
|
||||||
import { getServerActor } from '@server/models/application/application'
|
|
||||||
|
|
||||||
const overviewsRouter = express.Router()
|
const overviewsRouter = express.Router()
|
||||||
|
|
||||||
|
overviewsRouter.use(apiRateLimiter)
|
||||||
|
|
||||||
overviewsRouter.get('/videos',
|
overviewsRouter.get('/videos',
|
||||||
videosOverviewValidator,
|
videosOverviewValidator,
|
||||||
optionalAuthenticate,
|
optionalAuthenticate,
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { getFormattedObjects } from '@server/helpers/utils'
|
||||||
import { listAvailablePluginsFromIndex } from '@server/lib/plugins/plugin-index'
|
import { listAvailablePluginsFromIndex } from '@server/lib/plugins/plugin-index'
|
||||||
import { PluginManager } from '@server/lib/plugins/plugin-manager'
|
import { PluginManager } from '@server/lib/plugins/plugin-manager'
|
||||||
import {
|
import {
|
||||||
|
apiRateLimiter,
|
||||||
asyncMiddleware,
|
asyncMiddleware,
|
||||||
authenticate,
|
authenticate,
|
||||||
availablePluginsSortValidator,
|
availablePluginsSortValidator,
|
||||||
|
@ -35,6 +36,8 @@ import {
|
||||||
|
|
||||||
const pluginRouter = express.Router()
|
const pluginRouter = express.Router()
|
||||||
|
|
||||||
|
pluginRouter.use(apiRateLimiter)
|
||||||
|
|
||||||
pluginRouter.get('/available',
|
pluginRouter.get('/available',
|
||||||
openapiOperationDoc({ operationId: 'getAvailablePlugins' }),
|
openapiOperationDoc({ operationId: 'getAvailablePlugins' }),
|
||||||
authenticate,
|
authenticate,
|
||||||
|
|
|
@ -6,6 +6,8 @@ import { runnerRegistrationTokensRouter } from './registration-tokens'
|
||||||
|
|
||||||
const runnersRouter = express.Router()
|
const runnersRouter = express.Router()
|
||||||
|
|
||||||
|
// No api route limiter here, they are defined in child routers
|
||||||
|
|
||||||
runnersRouter.use('/', manageRunnersRouter)
|
runnersRouter.use('/', manageRunnersRouter)
|
||||||
runnersRouter.use('/', runnerJobsRouter)
|
runnersRouter.use('/', runnerJobsRouter)
|
||||||
runnersRouter.use('/', runnerJobFilesRouter)
|
runnersRouter.use('/', runnerJobFilesRouter)
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { logger, loggerTagsFactory } from '@server/helpers/logger'
|
||||||
import { proxifyHLS, proxifyWebTorrentFile } from '@server/lib/object-storage'
|
import { proxifyHLS, proxifyWebTorrentFile } from '@server/lib/object-storage'
|
||||||
import { VideoPathManager } from '@server/lib/video-path-manager'
|
import { VideoPathManager } from '@server/lib/video-path-manager'
|
||||||
import { getStudioTaskFilePath } from '@server/lib/video-studio'
|
import { getStudioTaskFilePath } from '@server/lib/video-studio'
|
||||||
import { asyncMiddleware } from '@server/middlewares'
|
import { apiRateLimiter, asyncMiddleware } from '@server/middlewares'
|
||||||
import { jobOfRunnerGetValidator } from '@server/middlewares/validators/runners'
|
import { jobOfRunnerGetValidator } from '@server/middlewares/validators/runners'
|
||||||
import {
|
import {
|
||||||
runnerJobGetVideoStudioTaskFileValidator,
|
runnerJobGetVideoStudioTaskFileValidator,
|
||||||
|
@ -16,18 +16,21 @@ const lTags = loggerTagsFactory('api', 'runner')
|
||||||
const runnerJobFilesRouter = express.Router()
|
const runnerJobFilesRouter = express.Router()
|
||||||
|
|
||||||
runnerJobFilesRouter.post('/jobs/:jobUUID/files/videos/:videoId/max-quality',
|
runnerJobFilesRouter.post('/jobs/:jobUUID/files/videos/:videoId/max-quality',
|
||||||
|
apiRateLimiter,
|
||||||
asyncMiddleware(jobOfRunnerGetValidator),
|
asyncMiddleware(jobOfRunnerGetValidator),
|
||||||
asyncMiddleware(runnerJobGetVideoTranscodingFileValidator),
|
asyncMiddleware(runnerJobGetVideoTranscodingFileValidator),
|
||||||
asyncMiddleware(getMaxQualityVideoFile)
|
asyncMiddleware(getMaxQualityVideoFile)
|
||||||
)
|
)
|
||||||
|
|
||||||
runnerJobFilesRouter.post('/jobs/:jobUUID/files/videos/:videoId/previews/max-quality',
|
runnerJobFilesRouter.post('/jobs/:jobUUID/files/videos/:videoId/previews/max-quality',
|
||||||
|
apiRateLimiter,
|
||||||
asyncMiddleware(jobOfRunnerGetValidator),
|
asyncMiddleware(jobOfRunnerGetValidator),
|
||||||
asyncMiddleware(runnerJobGetVideoTranscodingFileValidator),
|
asyncMiddleware(runnerJobGetVideoTranscodingFileValidator),
|
||||||
getMaxQualityVideoPreview
|
getMaxQualityVideoPreview
|
||||||
)
|
)
|
||||||
|
|
||||||
runnerJobFilesRouter.post('/jobs/:jobUUID/files/videos/:videoId/studio/task-files/:filename',
|
runnerJobFilesRouter.post('/jobs/:jobUUID/files/videos/:videoId/studio/task-files/:filename',
|
||||||
|
apiRateLimiter,
|
||||||
asyncMiddleware(jobOfRunnerGetValidator),
|
asyncMiddleware(jobOfRunnerGetValidator),
|
||||||
asyncMiddleware(runnerJobGetVideoTranscodingFileValidator),
|
asyncMiddleware(runnerJobGetVideoTranscodingFileValidator),
|
||||||
runnerJobGetVideoStudioTaskFileValidator,
|
runnerJobGetVideoStudioTaskFileValidator,
|
||||||
|
|
|
@ -7,6 +7,7 @@ import { MIMETYPES } from '@server/initializers/constants'
|
||||||
import { sequelizeTypescript } from '@server/initializers/database'
|
import { sequelizeTypescript } from '@server/initializers/database'
|
||||||
import { getRunnerJobHandlerClass, updateLastRunnerContact } from '@server/lib/runners'
|
import { getRunnerJobHandlerClass, updateLastRunnerContact } from '@server/lib/runners'
|
||||||
import {
|
import {
|
||||||
|
apiRateLimiter,
|
||||||
asyncMiddleware,
|
asyncMiddleware,
|
||||||
authenticate,
|
authenticate,
|
||||||
ensureUserHasRight,
|
ensureUserHasRight,
|
||||||
|
@ -69,11 +70,13 @@ const runnerJobsRouter = express.Router()
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
runnerJobsRouter.post('/jobs/request',
|
runnerJobsRouter.post('/jobs/request',
|
||||||
|
apiRateLimiter,
|
||||||
asyncMiddleware(getRunnerFromTokenValidator),
|
asyncMiddleware(getRunnerFromTokenValidator),
|
||||||
asyncMiddleware(requestRunnerJob)
|
asyncMiddleware(requestRunnerJob)
|
||||||
)
|
)
|
||||||
|
|
||||||
runnerJobsRouter.post('/jobs/:jobUUID/accept',
|
runnerJobsRouter.post('/jobs/:jobUUID/accept',
|
||||||
|
apiRateLimiter,
|
||||||
asyncMiddleware(runnerJobGetValidator),
|
asyncMiddleware(runnerJobGetValidator),
|
||||||
acceptRunnerJobValidator,
|
acceptRunnerJobValidator,
|
||||||
asyncMiddleware(getRunnerFromTokenValidator),
|
asyncMiddleware(getRunnerFromTokenValidator),
|
||||||
|
@ -81,6 +84,7 @@ runnerJobsRouter.post('/jobs/:jobUUID/accept',
|
||||||
)
|
)
|
||||||
|
|
||||||
runnerJobsRouter.post('/jobs/:jobUUID/abort',
|
runnerJobsRouter.post('/jobs/:jobUUID/abort',
|
||||||
|
apiRateLimiter,
|
||||||
asyncMiddleware(jobOfRunnerGetValidator),
|
asyncMiddleware(jobOfRunnerGetValidator),
|
||||||
abortRunnerJobValidator,
|
abortRunnerJobValidator,
|
||||||
asyncMiddleware(abortRunnerJob)
|
asyncMiddleware(abortRunnerJob)
|
||||||
|
@ -88,6 +92,7 @@ runnerJobsRouter.post('/jobs/:jobUUID/abort',
|
||||||
|
|
||||||
runnerJobsRouter.post('/jobs/:jobUUID/update',
|
runnerJobsRouter.post('/jobs/:jobUUID/update',
|
||||||
runnerJobUpdateVideoFiles,
|
runnerJobUpdateVideoFiles,
|
||||||
|
apiRateLimiter, // Has to be after multer middleware to parse runner token
|
||||||
asyncMiddleware(jobOfRunnerGetValidator),
|
asyncMiddleware(jobOfRunnerGetValidator),
|
||||||
updateRunnerJobValidator,
|
updateRunnerJobValidator,
|
||||||
asyncMiddleware(updateRunnerJobController)
|
asyncMiddleware(updateRunnerJobController)
|
||||||
|
@ -101,6 +106,7 @@ runnerJobsRouter.post('/jobs/:jobUUID/error',
|
||||||
|
|
||||||
runnerJobsRouter.post('/jobs/:jobUUID/success',
|
runnerJobsRouter.post('/jobs/:jobUUID/success',
|
||||||
postRunnerJobSuccessVideoFiles,
|
postRunnerJobSuccessVideoFiles,
|
||||||
|
apiRateLimiter, // Has to be after multer middleware to parse runner token
|
||||||
asyncMiddleware(jobOfRunnerGetValidator),
|
asyncMiddleware(jobOfRunnerGetValidator),
|
||||||
successRunnerJobValidator,
|
successRunnerJobValidator,
|
||||||
asyncMiddleware(postRunnerJobSuccess)
|
asyncMiddleware(postRunnerJobSuccess)
|
||||||
|
|
|
@ -2,6 +2,7 @@ import express from 'express'
|
||||||
import { logger, loggerTagsFactory } from '@server/helpers/logger'
|
import { logger, loggerTagsFactory } from '@server/helpers/logger'
|
||||||
import { generateRunnerToken } from '@server/helpers/token-generator'
|
import { generateRunnerToken } from '@server/helpers/token-generator'
|
||||||
import {
|
import {
|
||||||
|
apiRateLimiter,
|
||||||
asyncMiddleware,
|
asyncMiddleware,
|
||||||
authenticate,
|
authenticate,
|
||||||
ensureUserHasRight,
|
ensureUserHasRight,
|
||||||
|
@ -19,15 +20,18 @@ const lTags = loggerTagsFactory('api', 'runner')
|
||||||
const manageRunnersRouter = express.Router()
|
const manageRunnersRouter = express.Router()
|
||||||
|
|
||||||
manageRunnersRouter.post('/register',
|
manageRunnersRouter.post('/register',
|
||||||
|
apiRateLimiter,
|
||||||
asyncMiddleware(registerRunnerValidator),
|
asyncMiddleware(registerRunnerValidator),
|
||||||
asyncMiddleware(registerRunner)
|
asyncMiddleware(registerRunner)
|
||||||
)
|
)
|
||||||
manageRunnersRouter.post('/unregister',
|
manageRunnersRouter.post('/unregister',
|
||||||
|
apiRateLimiter,
|
||||||
asyncMiddleware(getRunnerFromTokenValidator),
|
asyncMiddleware(getRunnerFromTokenValidator),
|
||||||
asyncMiddleware(unregisterRunner)
|
asyncMiddleware(unregisterRunner)
|
||||||
)
|
)
|
||||||
|
|
||||||
manageRunnersRouter.delete('/:runnerId',
|
manageRunnersRouter.delete('/:runnerId',
|
||||||
|
apiRateLimiter,
|
||||||
authenticate,
|
authenticate,
|
||||||
ensureUserHasRight(UserRight.MANAGE_RUNNERS),
|
ensureUserHasRight(UserRight.MANAGE_RUNNERS),
|
||||||
asyncMiddleware(deleteRunnerValidator),
|
asyncMiddleware(deleteRunnerValidator),
|
||||||
|
@ -35,6 +39,7 @@ manageRunnersRouter.delete('/:runnerId',
|
||||||
)
|
)
|
||||||
|
|
||||||
manageRunnersRouter.get('/',
|
manageRunnersRouter.get('/',
|
||||||
|
apiRateLimiter,
|
||||||
authenticate,
|
authenticate,
|
||||||
ensureUserHasRight(UserRight.MANAGE_RUNNERS),
|
ensureUserHasRight(UserRight.MANAGE_RUNNERS),
|
||||||
paginationValidator,
|
paginationValidator,
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
import express from 'express'
|
import express from 'express'
|
||||||
|
import { logger, loggerTagsFactory } from '@server/helpers/logger'
|
||||||
import { generateRunnerRegistrationToken } from '@server/helpers/token-generator'
|
import { generateRunnerRegistrationToken } from '@server/helpers/token-generator'
|
||||||
import {
|
import {
|
||||||
|
apiRateLimiter,
|
||||||
asyncMiddleware,
|
asyncMiddleware,
|
||||||
authenticate,
|
authenticate,
|
||||||
ensureUserHasRight,
|
ensureUserHasRight,
|
||||||
|
@ -12,19 +14,20 @@ import {
|
||||||
import { deleteRegistrationTokenValidator } from '@server/middlewares/validators/runners'
|
import { deleteRegistrationTokenValidator } from '@server/middlewares/validators/runners'
|
||||||
import { RunnerRegistrationTokenModel } from '@server/models/runner/runner-registration-token'
|
import { RunnerRegistrationTokenModel } from '@server/models/runner/runner-registration-token'
|
||||||
import { HttpStatusCode, ListRunnerRegistrationTokensQuery, UserRight } from '@shared/models'
|
import { HttpStatusCode, ListRunnerRegistrationTokensQuery, UserRight } from '@shared/models'
|
||||||
import { logger, loggerTagsFactory } from '@server/helpers/logger'
|
|
||||||
|
|
||||||
const lTags = loggerTagsFactory('api', 'runner')
|
const lTags = loggerTagsFactory('api', 'runner')
|
||||||
|
|
||||||
const runnerRegistrationTokensRouter = express.Router()
|
const runnerRegistrationTokensRouter = express.Router()
|
||||||
|
|
||||||
runnerRegistrationTokensRouter.post('/registration-tokens/generate',
|
runnerRegistrationTokensRouter.post('/registration-tokens/generate',
|
||||||
|
apiRateLimiter,
|
||||||
authenticate,
|
authenticate,
|
||||||
ensureUserHasRight(UserRight.MANAGE_RUNNERS),
|
ensureUserHasRight(UserRight.MANAGE_RUNNERS),
|
||||||
asyncMiddleware(generateRegistrationToken)
|
asyncMiddleware(generateRegistrationToken)
|
||||||
)
|
)
|
||||||
|
|
||||||
runnerRegistrationTokensRouter.delete('/registration-tokens/:id',
|
runnerRegistrationTokensRouter.delete('/registration-tokens/:id',
|
||||||
|
apiRateLimiter,
|
||||||
authenticate,
|
authenticate,
|
||||||
ensureUserHasRight(UserRight.MANAGE_RUNNERS),
|
ensureUserHasRight(UserRight.MANAGE_RUNNERS),
|
||||||
asyncMiddleware(deleteRegistrationTokenValidator),
|
asyncMiddleware(deleteRegistrationTokenValidator),
|
||||||
|
@ -32,6 +35,7 @@ runnerRegistrationTokensRouter.delete('/registration-tokens/:id',
|
||||||
)
|
)
|
||||||
|
|
||||||
runnerRegistrationTokensRouter.get('/registration-tokens',
|
runnerRegistrationTokensRouter.get('/registration-tokens',
|
||||||
|
apiRateLimiter,
|
||||||
authenticate,
|
authenticate,
|
||||||
ensureUserHasRight(UserRight.MANAGE_RUNNERS),
|
ensureUserHasRight(UserRight.MANAGE_RUNNERS),
|
||||||
paginationValidator,
|
paginationValidator,
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
import express from 'express'
|
import express from 'express'
|
||||||
|
import { apiRateLimiter } from '@server/middlewares'
|
||||||
import { searchChannelsRouter } from './search-video-channels'
|
import { searchChannelsRouter } from './search-video-channels'
|
||||||
import { searchPlaylistsRouter } from './search-video-playlists'
|
import { searchPlaylistsRouter } from './search-video-playlists'
|
||||||
import { searchVideosRouter } from './search-videos'
|
import { searchVideosRouter } from './search-videos'
|
||||||
|
|
||||||
const searchRouter = express.Router()
|
const searchRouter = express.Router()
|
||||||
|
|
||||||
|
searchRouter.use(apiRateLimiter)
|
||||||
|
|
||||||
searchRouter.use('/', searchVideosRouter)
|
searchRouter.use('/', searchVideosRouter)
|
||||||
searchRouter.use('/', searchChannelsRouter)
|
searchRouter.use('/', searchChannelsRouter)
|
||||||
searchRouter.use('/', searchPlaylistsRouter)
|
searchRouter.use('/', searchPlaylistsRouter)
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import express from 'express'
|
import express from 'express'
|
||||||
|
import { apiRateLimiter } from '@server/middlewares'
|
||||||
import { contactRouter } from './contact'
|
import { contactRouter } from './contact'
|
||||||
import { debugRouter } from './debug'
|
import { debugRouter } from './debug'
|
||||||
import { serverFollowsRouter } from './follows'
|
import { serverFollowsRouter } from './follows'
|
||||||
|
@ -9,6 +10,8 @@ import { statsRouter } from './stats'
|
||||||
|
|
||||||
const serverRouter = express.Router()
|
const serverRouter = express.Router()
|
||||||
|
|
||||||
|
serverRouter.use(apiRateLimiter)
|
||||||
|
|
||||||
serverRouter.use('/', serverFollowsRouter)
|
serverRouter.use('/', serverFollowsRouter)
|
||||||
serverRouter.use('/', serverRedundancyRouter)
|
serverRouter.use('/', serverRedundancyRouter)
|
||||||
serverRouter.use('/', statsRouter)
|
serverRouter.use('/', statsRouter)
|
||||||
|
|
|
@ -15,6 +15,7 @@ import { Redis } from '../../../lib/redis'
|
||||||
import { buildUser, createUserAccountAndChannelAndPlaylist } from '../../../lib/user'
|
import { buildUser, createUserAccountAndChannelAndPlaylist } from '../../../lib/user'
|
||||||
import {
|
import {
|
||||||
adminUsersSortValidator,
|
adminUsersSortValidator,
|
||||||
|
apiRateLimiter,
|
||||||
asyncMiddleware,
|
asyncMiddleware,
|
||||||
asyncRetryTransactionMiddleware,
|
asyncRetryTransactionMiddleware,
|
||||||
authenticate,
|
authenticate,
|
||||||
|
@ -50,6 +51,9 @@ import { twoFactorRouter } from './two-factor'
|
||||||
const auditLogger = auditLoggerFactory('users')
|
const auditLogger = auditLoggerFactory('users')
|
||||||
|
|
||||||
const usersRouter = express.Router()
|
const usersRouter = express.Router()
|
||||||
|
|
||||||
|
usersRouter.use(apiRateLimiter)
|
||||||
|
|
||||||
usersRouter.use('/', emailVerificationRouter)
|
usersRouter.use('/', emailVerificationRouter)
|
||||||
usersRouter.use('/', registrationsRouter)
|
usersRouter.use('/', registrationsRouter)
|
||||||
usersRouter.use('/', twoFactorRouter)
|
usersRouter.use('/', twoFactorRouter)
|
||||||
|
|
|
@ -2,6 +2,7 @@ import express from 'express'
|
||||||
import { auditLoggerFactory, getAuditIdFromRes, VideoChannelSyncAuditView } from '@server/helpers/audit-logger'
|
import { auditLoggerFactory, getAuditIdFromRes, VideoChannelSyncAuditView } from '@server/helpers/audit-logger'
|
||||||
import { logger } from '@server/helpers/logger'
|
import { logger } from '@server/helpers/logger'
|
||||||
import {
|
import {
|
||||||
|
apiRateLimiter,
|
||||||
asyncMiddleware,
|
asyncMiddleware,
|
||||||
asyncRetryTransactionMiddleware,
|
asyncRetryTransactionMiddleware,
|
||||||
authenticate,
|
authenticate,
|
||||||
|
@ -17,6 +18,8 @@ import { HttpStatusCode, VideoChannelSyncState } from '@shared/models'
|
||||||
const videoChannelSyncRouter = express.Router()
|
const videoChannelSyncRouter = express.Router()
|
||||||
const auditLogger = auditLoggerFactory('channel-syncs')
|
const auditLogger = auditLoggerFactory('channel-syncs')
|
||||||
|
|
||||||
|
videoChannelSyncRouter.use(apiRateLimiter)
|
||||||
|
|
||||||
videoChannelSyncRouter.post('/',
|
videoChannelSyncRouter.post('/',
|
||||||
authenticate,
|
authenticate,
|
||||||
ensureSyncIsEnabled,
|
ensureSyncIsEnabled,
|
||||||
|
|
|
@ -19,6 +19,7 @@ import { JobQueue } from '../../lib/job-queue'
|
||||||
import { deleteLocalActorImageFile, updateLocalActorImageFiles } from '../../lib/local-actor'
|
import { deleteLocalActorImageFile, updateLocalActorImageFiles } from '../../lib/local-actor'
|
||||||
import { createLocalVideoChannel, federateAllVideosOfChannel } from '../../lib/video-channel'
|
import { createLocalVideoChannel, federateAllVideosOfChannel } from '../../lib/video-channel'
|
||||||
import {
|
import {
|
||||||
|
apiRateLimiter,
|
||||||
asyncMiddleware,
|
asyncMiddleware,
|
||||||
asyncRetryTransactionMiddleware,
|
asyncRetryTransactionMiddleware,
|
||||||
authenticate,
|
authenticate,
|
||||||
|
@ -57,6 +58,8 @@ const reqBannerFile = createReqFiles([ 'bannerfile' ], MIMETYPES.IMAGE.MIMETYPE_
|
||||||
|
|
||||||
const videoChannelRouter = express.Router()
|
const videoChannelRouter = express.Router()
|
||||||
|
|
||||||
|
videoChannelRouter.use(apiRateLimiter)
|
||||||
|
|
||||||
videoChannelRouter.get('/',
|
videoChannelRouter.get('/',
|
||||||
paginationValidator,
|
paginationValidator,
|
||||||
videoChannelsSortValidator,
|
videoChannelsSortValidator,
|
||||||
|
|
|
@ -25,6 +25,7 @@ import { sendCreateVideoPlaylist, sendDeleteVideoPlaylist, sendUpdateVideoPlayli
|
||||||
import { getLocalVideoPlaylistActivityPubUrl, getLocalVideoPlaylistElementActivityPubUrl } from '../../lib/activitypub/url'
|
import { getLocalVideoPlaylistActivityPubUrl, getLocalVideoPlaylistElementActivityPubUrl } from '../../lib/activitypub/url'
|
||||||
import { updatePlaylistMiniatureFromExisting } from '../../lib/thumbnail'
|
import { updatePlaylistMiniatureFromExisting } from '../../lib/thumbnail'
|
||||||
import {
|
import {
|
||||||
|
apiRateLimiter,
|
||||||
asyncMiddleware,
|
asyncMiddleware,
|
||||||
asyncRetryTransactionMiddleware,
|
asyncRetryTransactionMiddleware,
|
||||||
authenticate,
|
authenticate,
|
||||||
|
@ -52,6 +53,8 @@ const reqThumbnailFile = createReqFiles([ 'thumbnailfile' ], MIMETYPES.IMAGE.MIM
|
||||||
|
|
||||||
const videoPlaylistRouter = express.Router()
|
const videoPlaylistRouter = express.Router()
|
||||||
|
|
||||||
|
videoPlaylistRouter.use(apiRateLimiter)
|
||||||
|
|
||||||
videoPlaylistRouter.get('/privacies', listVideoPlaylistPrivacies)
|
videoPlaylistRouter.get('/privacies', listVideoPlaylistPrivacies)
|
||||||
|
|
||||||
videoPlaylistRouter.get('/',
|
videoPlaylistRouter.get('/',
|
||||||
|
|
|
@ -15,6 +15,7 @@ import { sequelizeTypescript } from '../../../initializers/database'
|
||||||
import { JobQueue } from '../../../lib/job-queue'
|
import { JobQueue } from '../../../lib/job-queue'
|
||||||
import { Hooks } from '../../../lib/plugins/hooks'
|
import { Hooks } from '../../../lib/plugins/hooks'
|
||||||
import {
|
import {
|
||||||
|
apiRateLimiter,
|
||||||
asyncMiddleware,
|
asyncMiddleware,
|
||||||
asyncRetryTransactionMiddleware,
|
asyncRetryTransactionMiddleware,
|
||||||
authenticate,
|
authenticate,
|
||||||
|
@ -50,6 +51,8 @@ import { viewRouter } from './view'
|
||||||
const auditLogger = auditLoggerFactory('videos')
|
const auditLogger = auditLoggerFactory('videos')
|
||||||
const videosRouter = express.Router()
|
const videosRouter = express.Router()
|
||||||
|
|
||||||
|
videosRouter.use(apiRateLimiter)
|
||||||
|
|
||||||
videosRouter.use('/', blacklistRouter)
|
videosRouter.use('/', blacklistRouter)
|
||||||
videosRouter.use('/', statsRouter)
|
videosRouter.use('/', statsRouter)
|
||||||
videosRouter.use('/', rateVideoRouter)
|
videosRouter.use('/', rateVideoRouter)
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import express from 'express'
|
import express from 'express'
|
||||||
import RateLimit, { Options as RateLimitHandlerOptions } from 'express-rate-limit'
|
import RateLimit, { Options as RateLimitHandlerOptions } from 'express-rate-limit'
|
||||||
|
import { CONFIG } from '@server/initializers/config'
|
||||||
import { RunnerModel } from '@server/models/runner/runner'
|
import { RunnerModel } from '@server/models/runner/runner'
|
||||||
import { UserRole } from '@shared/models'
|
import { UserRole } from '@shared/models'
|
||||||
import { optionalAuthenticate } from './auth'
|
import { optionalAuthenticate } from './auth'
|
||||||
|
@ -39,6 +40,11 @@ export function buildRateLimiter (options: {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const apiRateLimiter = buildRateLimiter({
|
||||||
|
windowMs: CONFIG.RATES_LIMIT.API.WINDOW_MS,
|
||||||
|
max: CONFIG.RATES_LIMIT.API.MAX
|
||||||
|
})
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Private
|
// Private
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
|
@ -14,7 +14,6 @@ import {
|
||||||
import {
|
import {
|
||||||
cleanupTests,
|
cleanupTests,
|
||||||
createSingleServer,
|
createSingleServer,
|
||||||
makePostBodyRequest,
|
|
||||||
PeerTubeServer,
|
PeerTubeServer,
|
||||||
setAccessTokensToServers,
|
setAccessTokensToServers,
|
||||||
setDefaultVideoChannel,
|
setDefaultVideoChannel,
|
||||||
|
@ -641,24 +640,47 @@ describe('Test runner common actions', function () {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should rate limit an unknown runner', async function () {
|
it('Should rate limit an unknown runner, but not a registered one', async function () {
|
||||||
const path = '/api/v1/ping'
|
this.timeout(60000)
|
||||||
const fields = { runnerToken: 'toto' }
|
|
||||||
|
await server.videos.quickUpload({ name: 'video' })
|
||||||
|
await waitJobs([ server ])
|
||||||
|
|
||||||
|
const { job } = await server.runnerJobs.autoAccept({ runnerToken })
|
||||||
|
|
||||||
for (let i = 0; i < 20; i++) {
|
for (let i = 0; i < 20; i++) {
|
||||||
try {
|
try {
|
||||||
await makePostBodyRequest({ url: server.url, path, fields, expectedStatus: HttpStatusCode.OK_200 })
|
await server.runnerJobs.request({ runnerToken })
|
||||||
|
await server.runnerJobs.update({ runnerToken, jobToken: job.jobToken, jobUUID: job.uuid })
|
||||||
} catch {}
|
} catch {}
|
||||||
}
|
}
|
||||||
|
|
||||||
await makePostBodyRequest({ url: server.url, path, fields, expectedStatus: HttpStatusCode.TOO_MANY_REQUESTS_429 })
|
// Invalid
|
||||||
})
|
{
|
||||||
|
await server.runnerJobs.request({ runnerToken: 'toto', expectedStatus: HttpStatusCode.TOO_MANY_REQUESTS_429 })
|
||||||
|
await server.runnerJobs.update({
|
||||||
|
runnerToken: 'toto',
|
||||||
|
jobToken: job.jobToken,
|
||||||
|
jobUUID: job.uuid,
|
||||||
|
expectedStatus: HttpStatusCode.TOO_MANY_REQUESTS_429
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
it('Should not rate limit a registered runner', async function () {
|
// Not provided
|
||||||
const path = '/api/v1/ping'
|
{
|
||||||
|
await server.runnerJobs.request({ runnerToken: undefined, expectedStatus: HttpStatusCode.TOO_MANY_REQUESTS_429 })
|
||||||
|
await server.runnerJobs.update({
|
||||||
|
runnerToken: undefined,
|
||||||
|
jobToken: job.jobToken,
|
||||||
|
jobUUID: job.uuid,
|
||||||
|
expectedStatus: HttpStatusCode.TOO_MANY_REQUESTS_429
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
for (let i = 0; i < 20; i++) {
|
// Registered
|
||||||
await makePostBodyRequest({ url: server.url, path, fields: { runnerToken }, expectedStatus: HttpStatusCode.OK_200 })
|
{
|
||||||
|
await server.runnerJobs.request({ runnerToken })
|
||||||
|
await server.runnerJobs.update({ runnerToken, jobToken: job.jobToken, jobUUID: job.uuid })
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in New Issue