1
0
Fork 0
mirror of https://github.com/Chocobozzz/PeerTube.git synced 2025-10-04 18:29:27 +02:00

Refactor account/channel manage checks

Use a more robust approach by requiring the caller to choose if it needs
to check the actor is local and/or the user can manage it
This commit is contained in:
Chocobozzz 2025-04-10 08:51:23 +02:00
parent a1279d7eb5
commit 334ad174a9
No known key found for this signature in database
GPG key ID: 583A612D890159BE
25 changed files with 420 additions and 391 deletions

View file

@ -140,10 +140,11 @@ describe('Test videos API validator', function () {
} }
{ {
const { total, data } = await server.videos.listMyVideos({ token: userAccessToken, channelId: server.store.channel.id }) await server.videos.listMyVideos({
token: userAccessToken,
expect(total).to.equal(0) channelId: server.store.channel.id,
expect(data).to.have.lengthOf(0) expectedStatus: HttpStatusCode.FORBIDDEN_403
})
} }
} }

View file

@ -37,14 +37,13 @@ import {
import { import {
activityPubRateLimiter, activityPubRateLimiter,
asyncMiddleware, asyncMiddleware,
ensureIsLocalChannel,
executeIfActivityPub, executeIfActivityPub,
localAccountValidator, videoChannelsHandleValidatorFactory,
videoChannelsNameWithHostValidator,
videosCustomGetValidator, videosCustomGetValidator,
videosShareValidator videosShareValidator
} from '../../middlewares/index.js' } from '../../middlewares/index.js'
import { import {
accountHandleGetValidatorFactory,
getAccountVideoRateValidatorFactory, getAccountVideoRateValidatorFactory,
getVideoLocalViewerValidator, getVideoLocalViewerValidator,
videoCommentGetValidator videoCommentGetValidator
@ -68,35 +67,40 @@ activityPubClientRouter.get(
[ '/accounts?/:name', '/accounts?/:name/video-channels', '/a/:name', '/a/:name/video-channels' ], [ '/accounts?/:name', '/accounts?/:name/video-channels', '/a/:name', '/a/:name/video-channels' ],
executeIfActivityPub, executeIfActivityPub,
activityPubRateLimiter, activityPubRateLimiter,
asyncMiddleware(localAccountValidator), asyncMiddleware(accountHandleGetValidatorFactory({ checkIsLocal: true, checkManage: false })),
asyncMiddleware(accountController) asyncMiddleware(accountController)
) )
activityPubClientRouter.get('/accounts?/:name/followers', activityPubClientRouter.get(
'/accounts?/:name/followers',
executeIfActivityPub, executeIfActivityPub,
activityPubRateLimiter, activityPubRateLimiter,
asyncMiddleware(localAccountValidator), asyncMiddleware(accountHandleGetValidatorFactory({ checkIsLocal: true, checkManage: false })),
asyncMiddleware(accountFollowersController) asyncMiddleware(accountFollowersController)
) )
activityPubClientRouter.get('/accounts?/:name/following', activityPubClientRouter.get(
'/accounts?/:name/following',
executeIfActivityPub, executeIfActivityPub,
activityPubRateLimiter, activityPubRateLimiter,
asyncMiddleware(localAccountValidator), asyncMiddleware(accountHandleGetValidatorFactory({ checkIsLocal: true, checkManage: false })),
asyncMiddleware(accountFollowingController) asyncMiddleware(accountFollowingController)
) )
activityPubClientRouter.get('/accounts?/:name/playlists', activityPubClientRouter.get(
'/accounts?/:name/playlists',
executeIfActivityPub, executeIfActivityPub,
activityPubRateLimiter, activityPubRateLimiter,
asyncMiddleware(localAccountValidator), asyncMiddleware(accountHandleGetValidatorFactory({ checkIsLocal: true, checkManage: false })),
asyncMiddleware(accountPlaylistsController) asyncMiddleware(accountPlaylistsController)
) )
activityPubClientRouter.get('/accounts?/:name/likes/:videoId', activityPubClientRouter.get(
'/accounts?/:name/likes/:videoId',
executeIfActivityPub, executeIfActivityPub,
activityPubRateLimiter, activityPubRateLimiter,
cacheRoute(ROUTE_CACHE_LIFETIME.ACTIVITY_PUB.VIDEOS), cacheRoute(ROUTE_CACHE_LIFETIME.ACTIVITY_PUB.VIDEOS),
asyncMiddleware(getAccountVideoRateValidatorFactory('like')), asyncMiddleware(getAccountVideoRateValidatorFactory('like')),
asyncMiddleware(getAccountVideoRateFactory('like')) asyncMiddleware(getAccountVideoRateFactory('like'))
) )
activityPubClientRouter.get('/accounts?/:name/dislikes/:videoId', activityPubClientRouter.get(
'/accounts?/:name/dislikes/:videoId',
executeIfActivityPub, executeIfActivityPub,
activityPubRateLimiter, activityPubRateLimiter,
cacheRoute(ROUTE_CACHE_LIFETIME.ACTIVITY_PUB.VIDEOS), cacheRoute(ROUTE_CACHE_LIFETIME.ACTIVITY_PUB.VIDEOS),
@ -106,19 +110,22 @@ activityPubClientRouter.get('/accounts?/:name/dislikes/:videoId',
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
activityPubClientRouter.get('/videos/watch/:id/comments', activityPubClientRouter.get(
'/videos/watch/:id/comments',
executeIfActivityPub, executeIfActivityPub,
activityPubRateLimiter, activityPubRateLimiter,
asyncMiddleware(videosCustomGetValidator('only-video-and-blacklist')), asyncMiddleware(videosCustomGetValidator('only-video-and-blacklist')),
asyncMiddleware(videoCommentsController) asyncMiddleware(videoCommentsController)
) )
activityPubClientRouter.get('/videos/watch/:videoId/comments/:commentId/approve-reply', activityPubClientRouter.get(
'/videos/watch/:videoId/comments/:commentId/approve-reply',
executeIfActivityPub, executeIfActivityPub,
activityPubRateLimiter, activityPubRateLimiter,
asyncMiddleware(videoCommentGetValidator), asyncMiddleware(videoCommentGetValidator),
asyncMiddleware(videoCommentApprovedController) asyncMiddleware(videoCommentApprovedController)
) )
activityPubClientRouter.get('/videos/watch/:videoId/comments/:commentId/activity', activityPubClientRouter.get(
'/videos/watch/:videoId/comments/:commentId/activity',
executeIfActivityPub, executeIfActivityPub,
activityPubRateLimiter, activityPubRateLimiter,
asyncMiddleware(videoCommentGetValidator), asyncMiddleware(videoCommentGetValidator),
@ -142,31 +149,36 @@ activityPubClientRouter.get(
asyncMiddleware(videosCustomGetValidator('all')), asyncMiddleware(videosCustomGetValidator('all')),
asyncMiddleware(videoController) asyncMiddleware(videoController)
) )
activityPubClientRouter.get('/videos/watch/:id/activity', activityPubClientRouter.get(
'/videos/watch/:id/activity',
executeIfActivityPub, executeIfActivityPub,
activityPubRateLimiter, activityPubRateLimiter,
asyncMiddleware(videosCustomGetValidator('all')), asyncMiddleware(videosCustomGetValidator('all')),
asyncMiddleware(videoController) asyncMiddleware(videoController)
) )
activityPubClientRouter.get('/videos/watch/:id/announces', activityPubClientRouter.get(
'/videos/watch/:id/announces',
executeIfActivityPub, executeIfActivityPub,
activityPubRateLimiter, activityPubRateLimiter,
asyncMiddleware(videosCustomGetValidator('only-video-and-blacklist')), asyncMiddleware(videosCustomGetValidator('only-video-and-blacklist')),
asyncMiddleware(videoAnnouncesController) asyncMiddleware(videoAnnouncesController)
) )
activityPubClientRouter.get('/videos/watch/:id/announces/:actorId', activityPubClientRouter.get(
'/videos/watch/:id/announces/:actorId',
executeIfActivityPub, executeIfActivityPub,
activityPubRateLimiter, activityPubRateLimiter,
asyncMiddleware(videosShareValidator), asyncMiddleware(videosShareValidator),
asyncMiddleware(videoAnnounceController) asyncMiddleware(videoAnnounceController)
) )
activityPubClientRouter.get('/videos/watch/:id/likes', activityPubClientRouter.get(
'/videos/watch/:id/likes',
executeIfActivityPub, executeIfActivityPub,
activityPubRateLimiter, activityPubRateLimiter,
asyncMiddleware(videosCustomGetValidator('only-video-and-blacklist')), asyncMiddleware(videosCustomGetValidator('only-video-and-blacklist')),
asyncMiddleware(videoLikesController) asyncMiddleware(videoLikesController)
) )
activityPubClientRouter.get('/videos/watch/:id/dislikes', activityPubClientRouter.get(
'/videos/watch/:id/dislikes',
executeIfActivityPub, executeIfActivityPub,
activityPubRateLimiter, activityPubRateLimiter,
asyncMiddleware(videosCustomGetValidator('only-video-and-blacklist')), asyncMiddleware(videosCustomGetValidator('only-video-and-blacklist')),
@ -183,7 +195,8 @@ InternalEventEmitter.Instance.on('chapters-updated', ({ video }) => {
chaptersApiCache.clearGroupSafe(buildAPVideoChaptersGroupsCache({ videoId: video.uuid })) chaptersApiCache.clearGroupSafe(buildAPVideoChaptersGroupsCache({ videoId: video.uuid }))
}) })
activityPubClientRouter.get('/videos/watch/:id/chapters', activityPubClientRouter.get(
'/videos/watch/:id/chapters',
executeIfActivityPub, executeIfActivityPub,
activityPubRateLimiter, activityPubRateLimiter,
apVideoChaptersSetCacheKey, apVideoChaptersSetCacheKey,
@ -198,33 +211,33 @@ activityPubClientRouter.get(
[ '/video-channels/:nameWithHost', '/video-channels/:nameWithHost/videos', '/c/:nameWithHost', '/c/:nameWithHost/videos' ], [ '/video-channels/:nameWithHost', '/video-channels/:nameWithHost/videos', '/c/:nameWithHost', '/c/:nameWithHost/videos' ],
executeIfActivityPub, executeIfActivityPub,
activityPubRateLimiter, activityPubRateLimiter,
asyncMiddleware(videoChannelsNameWithHostValidator), asyncMiddleware(videoChannelsHandleValidatorFactory({ checkIsLocal: true, checkManage: false })),
ensureIsLocalChannel,
asyncMiddleware(videoChannelController) asyncMiddleware(videoChannelController)
) )
activityPubClientRouter.get('/video-channels/:nameWithHost/followers', activityPubClientRouter.get(
'/video-channels/:nameWithHost/followers',
executeIfActivityPub, executeIfActivityPub,
activityPubRateLimiter, activityPubRateLimiter,
asyncMiddleware(videoChannelsNameWithHostValidator), asyncMiddleware(videoChannelsHandleValidatorFactory({ checkIsLocal: true, checkManage: false })),
ensureIsLocalChannel,
asyncMiddleware(videoChannelFollowersController) asyncMiddleware(videoChannelFollowersController)
) )
activityPubClientRouter.get('/video-channels/:nameWithHost/following', activityPubClientRouter.get(
'/video-channels/:nameWithHost/following',
executeIfActivityPub, executeIfActivityPub,
activityPubRateLimiter, activityPubRateLimiter,
asyncMiddleware(videoChannelsNameWithHostValidator), asyncMiddleware(videoChannelsHandleValidatorFactory({ checkIsLocal: true, checkManage: false })),
ensureIsLocalChannel,
asyncMiddleware(videoChannelFollowingController) asyncMiddleware(videoChannelFollowingController)
) )
activityPubClientRouter.get('/video-channels/:nameWithHost/playlists', activityPubClientRouter.get(
'/video-channels/:nameWithHost/playlists',
executeIfActivityPub, executeIfActivityPub,
activityPubRateLimiter, activityPubRateLimiter,
asyncMiddleware(videoChannelsNameWithHostValidator), asyncMiddleware(videoChannelsHandleValidatorFactory({ checkIsLocal: true, checkManage: false })),
ensureIsLocalChannel,
asyncMiddleware(videoChannelPlaylistsController) asyncMiddleware(videoChannelPlaylistsController)
) )
activityPubClientRouter.get('/redundancy/streaming-playlists/:streamingPlaylistType/:videoId', activityPubClientRouter.get(
'/redundancy/streaming-playlists/:streamingPlaylistType/:videoId',
executeIfActivityPub, executeIfActivityPub,
activityPubRateLimiter, activityPubRateLimiter,
asyncMiddleware(videoPlaylistRedundancyGetValidator), asyncMiddleware(videoPlaylistRedundancyGetValidator),
@ -238,14 +251,16 @@ activityPubClientRouter.get(
asyncMiddleware(videoPlaylistsGetValidator('all')), asyncMiddleware(videoPlaylistsGetValidator('all')),
asyncMiddleware(videoPlaylistController) asyncMiddleware(videoPlaylistController)
) )
activityPubClientRouter.get('/video-playlists/:playlistId/videos/:playlistElementId', activityPubClientRouter.get(
'/video-playlists/:playlistId/videos/:playlistElementId',
executeIfActivityPub, executeIfActivityPub,
activityPubRateLimiter, activityPubRateLimiter,
asyncMiddleware(videoPlaylistElementAPGetValidator), asyncMiddleware(videoPlaylistElementAPGetValidator),
asyncMiddleware(videoPlaylistElementController) asyncMiddleware(videoPlaylistElementController)
) )
activityPubClientRouter.get('/videos/local-viewer/:localViewerId', activityPubClientRouter.get(
'/videos/local-viewer/:localViewerId',
executeIfActivityPub, executeIfActivityPub,
activityPubRateLimiter, activityPubRateLimiter,
asyncMiddleware(getVideoLocalViewerValidator), asyncMiddleware(getVideoLocalViewerValidator),

View file

@ -1,22 +1,22 @@
import express from 'express'
import { Activity, ActivityPubCollection, ActivityPubOrderedCollection, HttpStatusCode, RootActivity } from '@peertube/peertube-models' import { Activity, ActivityPubCollection, ActivityPubOrderedCollection, HttpStatusCode, RootActivity } from '@peertube/peertube-models'
import { InboxManager } from '@server/lib/activitypub/inbox-manager.js' import { InboxManager } from '@server/lib/activitypub/inbox-manager.js'
import express from 'express'
import { isActivityValid } from '../../helpers/custom-validators/activitypub/activity.js' import { isActivityValid } from '../../helpers/custom-validators/activitypub/activity.js'
import { logger } from '../../helpers/logger.js' import { logger } from '../../helpers/logger.js'
import { import {
accountHandleGetValidatorFactory,
activityPubRateLimiter, activityPubRateLimiter,
asyncMiddleware, asyncMiddleware,
checkSignature, checkSignature,
ensureIsLocalChannel,
localAccountValidator,
signatureValidator, signatureValidator,
videoChannelsNameWithHostValidator videoChannelsHandleValidatorFactory
} from '../../middlewares/index.js' } from '../../middlewares/index.js'
import { activityPubValidator } from '../../middlewares/validators/activitypub/activity.js' import { activityPubValidator } from '../../middlewares/validators/activitypub/activity.js'
const inboxRouter = express.Router() const inboxRouter = express.Router()
inboxRouter.post('/inbox', inboxRouter.post(
'/inbox',
activityPubRateLimiter, activityPubRateLimiter,
signatureValidator, signatureValidator,
asyncMiddleware(checkSignature), asyncMiddleware(checkSignature),
@ -24,21 +24,22 @@ inboxRouter.post('/inbox',
inboxController inboxController
) )
inboxRouter.post('/accounts/:name/inbox', inboxRouter.post(
'/accounts/:name/inbox',
activityPubRateLimiter, activityPubRateLimiter,
signatureValidator, signatureValidator,
asyncMiddleware(checkSignature), asyncMiddleware(checkSignature),
asyncMiddleware(localAccountValidator), asyncMiddleware(accountHandleGetValidatorFactory({ checkIsLocal: true, checkManage: false })),
asyncMiddleware(activityPubValidator), asyncMiddleware(activityPubValidator),
inboxController inboxController
) )
inboxRouter.post('/video-channels/:nameWithHost/inbox', inboxRouter.post(
'/video-channels/:nameWithHost/inbox',
activityPubRateLimiter, activityPubRateLimiter,
signatureValidator, signatureValidator,
asyncMiddleware(checkSignature), asyncMiddleware(checkSignature),
asyncMiddleware(videoChannelsNameWithHostValidator), asyncMiddleware(videoChannelsHandleValidatorFactory({ checkIsLocal: true, checkManage: false })),
ensureIsLocalChannel,
asyncMiddleware(activityPubValidator), asyncMiddleware(activityPubValidator),
inboxController inboxController
) )

View file

@ -1,18 +1,17 @@
import express from 'express'
import { Activity, VideoPrivacy } from '@peertube/peertube-models' import { Activity, VideoPrivacy } from '@peertube/peertube-models'
import { activityPubContextify } from '@server/helpers/activity-pub-utils.js' import { activityPubContextify } from '@server/helpers/activity-pub-utils.js'
import { activityPubCollectionPagination } from '@server/lib/activitypub/collection.js' import { activityPubCollectionPagination } from '@server/lib/activitypub/collection.js'
import { getContextFilter } from '@server/lib/activitypub/context.js' import { getContextFilter } from '@server/lib/activitypub/context.js'
import { MActorLight } from '@server/types/models/index.js' import { MActorLight } from '@server/types/models/index.js'
import express from 'express'
import { logger } from '../../helpers/logger.js' import { logger } from '../../helpers/logger.js'
import { buildAudience } from '../../lib/activitypub/audience.js' import { buildAudience } from '../../lib/activitypub/audience.js'
import { buildAnnounceActivity, buildCreateActivity } from '../../lib/activitypub/send/index.js' import { buildAnnounceActivity, buildCreateActivity } from '../../lib/activitypub/send/index.js'
import { import {
accountHandleGetValidatorFactory,
activityPubRateLimiter, activityPubRateLimiter,
asyncMiddleware, asyncMiddleware,
ensureIsLocalChannel, videoChannelsHandleValidatorFactory
localAccountValidator,
videoChannelsNameWithHostValidator
} from '../../middlewares/index.js' } from '../../middlewares/index.js'
import { apPaginationValidator } from '../../middlewares/validators/activitypub/index.js' import { apPaginationValidator } from '../../middlewares/validators/activitypub/index.js'
import { VideoModel } from '../../models/video/video.js' import { VideoModel } from '../../models/video/video.js'
@ -20,18 +19,19 @@ import { activityPubResponse } from './utils.js'
const outboxRouter = express.Router() const outboxRouter = express.Router()
outboxRouter.get('/accounts/:name/outbox', outboxRouter.get(
'/accounts/:name/outbox',
activityPubRateLimiter, activityPubRateLimiter,
apPaginationValidator, apPaginationValidator,
localAccountValidator, accountHandleGetValidatorFactory({ checkIsLocal: true, checkManage: false }),
asyncMiddleware(outboxController) asyncMiddleware(outboxController)
) )
outboxRouter.get('/video-channels/:nameWithHost/outbox', outboxRouter.get(
'/video-channels/:nameWithHost/outbox',
activityPubRateLimiter, activityPubRateLimiter,
apPaginationValidator, apPaginationValidator,
asyncMiddleware(videoChannelsNameWithHostValidator), asyncMiddleware(videoChannelsHandleValidatorFactory({ checkIsLocal: true, checkManage: false })),
ensureIsLocalChannel,
asyncMiddleware(outboxController) asyncMiddleware(outboxController)
) )

View file

@ -1,8 +1,8 @@
import express from 'express'
import { pickCommonVideoQuery } from '@server/helpers/query.js' import { pickCommonVideoQuery } from '@server/helpers/query.js'
import { ActorFollowModel } from '@server/models/actor/actor-follow.js' import { ActorFollowModel } from '@server/models/actor/actor-follow.js'
import { getServerActor } from '@server/models/application/application.js' import { getServerActor } from '@server/models/application/application.js'
import { VideoChannelSyncModel } from '@server/models/video/video-channel-sync.js' import { VideoChannelSyncModel } from '@server/models/video/video-channel-sync.js'
import express from 'express'
import { buildNSFWFilter, getCountVideos, isUserAbleToSearchRemoteURI } from '../../helpers/express-utils.js' import { buildNSFWFilter, getCountVideos, isUserAbleToSearchRemoteURI } from '../../helpers/express-utils.js'
import { getFormattedObjects } from '../../helpers/utils.js' import { getFormattedObjects } from '../../helpers/utils.js'
import { JobQueue } from '../../lib/job-queue/index.js' import { JobQueue } from '../../lib/job-queue/index.js'
@ -22,29 +22,28 @@ import {
videoRatingValidator videoRatingValidator
} from '../../middlewares/index.js' } from '../../middlewares/index.js'
import { import {
accountNameWithHostGetValidator, accountHandleGetValidatorFactory,
accountsFollowersSortValidator, accountsFollowersSortValidator,
accountsSortValidator, accountsSortValidator,
ensureAuthUserOwnsAccountValidator,
ensureCanManageChannelOrAccount,
videoChannelsSortValidator, videoChannelsSortValidator,
videoChannelStatsValidator, videoChannelStatsValidator,
videoChannelSyncsSortValidator, videoChannelSyncsSortValidator,
videosSortValidator videosSortValidator
} from '../../middlewares/validators/index.js' } from '../../middlewares/validators/index.js'
import { commonVideoPlaylistFiltersValidator, videoPlaylistsSearchValidator } from '../../middlewares/validators/videos/video-playlists.js' import { commonVideoPlaylistFiltersValidator, videoPlaylistsSearchValidator } from '../../middlewares/validators/videos/video-playlists.js'
import { AccountModel } from '../../models/account/account.js'
import { AccountVideoRateModel } from '../../models/account/account-video-rate.js' import { AccountVideoRateModel } from '../../models/account/account-video-rate.js'
import { AccountModel } from '../../models/account/account.js'
import { guessAdditionalAttributesFromQuery } from '../../models/video/formatter/index.js' import { guessAdditionalAttributesFromQuery } from '../../models/video/formatter/index.js'
import { VideoModel } from '../../models/video/video.js'
import { VideoChannelModel } from '../../models/video/video-channel.js' import { VideoChannelModel } from '../../models/video/video-channel.js'
import { VideoPlaylistModel } from '../../models/video/video-playlist.js' import { VideoPlaylistModel } from '../../models/video/video-playlist.js'
import { VideoModel } from '../../models/video/video.js'
const accountsRouter = express.Router() const accountsRouter = express.Router()
accountsRouter.use(apiRateLimiter) accountsRouter.use(apiRateLimiter)
accountsRouter.get('/', accountsRouter.get(
'/',
paginationValidator, paginationValidator,
accountsSortValidator, accountsSortValidator,
setDefaultSort, setDefaultSort,
@ -52,13 +51,15 @@ accountsRouter.get('/',
asyncMiddleware(listAccounts) asyncMiddleware(listAccounts)
) )
accountsRouter.get('/:accountName', accountsRouter.get(
asyncMiddleware(accountNameWithHostGetValidator), '/:accountName',
asyncMiddleware(accountHandleGetValidatorFactory({ checkIsLocal: false, checkManage: false })),
getAccount getAccount
) )
accountsRouter.get('/:accountName/videos', accountsRouter.get(
asyncMiddleware(accountNameWithHostGetValidator), '/:accountName/videos',
asyncMiddleware(accountHandleGetValidatorFactory({ checkIsLocal: false, checkManage: false })),
paginationValidator, paginationValidator,
videosSortValidator, videosSortValidator,
setDefaultVideosSort, setDefaultVideosSort,
@ -68,8 +69,9 @@ accountsRouter.get('/:accountName/videos',
asyncMiddleware(listAccountVideos) asyncMiddleware(listAccountVideos)
) )
accountsRouter.get('/:accountName/video-channels', accountsRouter.get(
asyncMiddleware(accountNameWithHostGetValidator), '/:accountName/video-channels',
asyncMiddleware(accountHandleGetValidatorFactory({ checkIsLocal: false, checkManage: false })),
videoChannelStatsValidator, videoChannelStatsValidator,
paginationValidator, paginationValidator,
videoChannelsSortValidator, videoChannelsSortValidator,
@ -78,20 +80,10 @@ accountsRouter.get('/:accountName/video-channels',
asyncMiddleware(listAccountChannels) asyncMiddleware(listAccountChannels)
) )
accountsRouter.get('/:accountName/video-channel-syncs', accountsRouter.get(
authenticate, '/:accountName/video-playlists',
asyncMiddleware(accountNameWithHostGetValidator),
ensureCanManageChannelOrAccount,
paginationValidator,
videoChannelSyncsSortValidator,
setDefaultSort,
setDefaultPagination,
asyncMiddleware(listAccountChannelsSync)
)
accountsRouter.get('/:accountName/video-playlists',
optionalAuthenticate, optionalAuthenticate,
asyncMiddleware(accountNameWithHostGetValidator), asyncMiddleware(accountHandleGetValidatorFactory({ checkIsLocal: false, checkManage: false })),
paginationValidator, paginationValidator,
videoPlaylistsSortValidator, videoPlaylistsSortValidator,
setDefaultSort, setDefaultSort,
@ -101,10 +93,21 @@ accountsRouter.get('/:accountName/video-playlists',
asyncMiddleware(listAccountPlaylists) asyncMiddleware(listAccountPlaylists)
) )
accountsRouter.get('/:accountName/ratings', accountsRouter.get(
'/:accountName/video-channel-syncs',
authenticate, authenticate,
asyncMiddleware(accountNameWithHostGetValidator), asyncMiddleware(accountHandleGetValidatorFactory({ checkIsLocal: true, checkManage: true })),
ensureAuthUserOwnsAccountValidator, paginationValidator,
videoChannelSyncsSortValidator,
setDefaultSort,
setDefaultPagination,
asyncMiddleware(listAccountChannelsSync)
)
accountsRouter.get(
'/:accountName/ratings',
authenticate,
asyncMiddleware(accountHandleGetValidatorFactory({ checkIsLocal: true, checkManage: true })),
paginationValidator, paginationValidator,
videoRatesSortValidator, videoRatesSortValidator,
setDefaultSort, setDefaultSort,
@ -113,10 +116,10 @@ accountsRouter.get('/:accountName/ratings',
asyncMiddleware(listAccountRatings) asyncMiddleware(listAccountRatings)
) )
accountsRouter.get('/:accountName/followers', accountsRouter.get(
'/:accountName/followers',
authenticate, authenticate,
asyncMiddleware(accountNameWithHostGetValidator), asyncMiddleware(accountHandleGetValidatorFactory({ checkIsLocal: true, checkManage: true })),
ensureAuthUserOwnsAccountValidator,
paginationValidator, paginationValidator,
accountsFollowersSortValidator, accountsFollowersSortValidator,
setDefaultSort, setDefaultSort,

View file

@ -1,10 +1,10 @@
import express from 'express' import { pick } from '@peertube/peertube-core-utils'
import { HttpStatusCode, UserCreate, UserCreateResult, UserRight, UserUpdate } from '@peertube/peertube-models'
import { tokensRouter } from '@server/controllers/api/users/token.js' import { tokensRouter } from '@server/controllers/api/users/token.js'
import { Hooks } from '@server/lib/plugins/hooks.js' import { Hooks } from '@server/lib/plugins/hooks.js'
import { OAuthTokenModel } from '@server/models/oauth/oauth-token.js' import { OAuthTokenModel } from '@server/models/oauth/oauth-token.js'
import { MUserAccountDefault } from '@server/types/models/index.js' import { MUserAccountDefault } from '@server/types/models/index.js'
import { pick } from '@peertube/peertube-core-utils' import express from 'express'
import { HttpStatusCode, UserCreate, UserCreateResult, UserRight, UserUpdate } from '@peertube/peertube-models'
import { auditLoggerFactory, getAuditIdFromRes, UserAuditView } from '../../../helpers/audit-logger.js' import { auditLoggerFactory, getAuditIdFromRes, UserAuditView } from '../../../helpers/audit-logger.js'
import { logger, loggerTagsFactory } from '../../../helpers/logger.js' import { logger, loggerTagsFactory } from '../../../helpers/logger.js'
import { generateRandomString, getFormattedObjects } from '../../../helpers/utils.js' import { generateRandomString, getFormattedObjects } from '../../../helpers/utils.js'
@ -31,9 +31,8 @@ import {
usersUpdateValidator usersUpdateValidator
} from '../../../middlewares/index.js' } from '../../../middlewares/index.js'
import { import {
ensureCanModerateUser,
usersAskResetPasswordValidator, usersAskResetPasswordValidator,
usersBlockingValidator, usersBlockToggleValidator,
usersResetPasswordValidator usersResetPasswordValidator
} from '../../../middlewares/validators/index.js' } from '../../../middlewares/validators/index.js'
import { UserModel } from '../../../models/user/user.js' import { UserModel } from '../../../models/user/user.js'
@ -71,12 +70,10 @@ usersRouter.use('/', myVideoPlaylistsRouter)
usersRouter.use('/', myAbusesRouter) usersRouter.use('/', myAbusesRouter)
usersRouter.use('/', meRouter) usersRouter.use('/', meRouter)
usersRouter.get('/autocomplete', usersRouter.get('/autocomplete', userAutocompleteValidator, asyncMiddleware(autocompleteUsers))
userAutocompleteValidator,
asyncMiddleware(autocompleteUsers)
)
usersRouter.get('/', usersRouter.get(
'/',
authenticate, authenticate,
ensureUserHasRight(UserRight.MANAGE_USERS), ensureUserHasRight(UserRight.MANAGE_USERS),
paginationValidator, paginationValidator,
@ -87,60 +84,50 @@ usersRouter.get('/',
asyncMiddleware(listUsers) asyncMiddleware(listUsers)
) )
usersRouter.post('/:id/block', usersRouter.post(
'/:id/block',
authenticate, authenticate,
ensureUserHasRight(UserRight.MANAGE_USERS), ensureUserHasRight(UserRight.MANAGE_USERS),
asyncMiddleware(usersBlockingValidator), asyncMiddleware(usersBlockToggleValidator),
ensureCanModerateUser,
asyncMiddleware(blockUser) asyncMiddleware(blockUser)
) )
usersRouter.post('/:id/unblock', usersRouter.post(
'/:id/unblock',
authenticate, authenticate,
ensureUserHasRight(UserRight.MANAGE_USERS), ensureUserHasRight(UserRight.MANAGE_USERS),
asyncMiddleware(usersBlockingValidator), asyncMiddleware(usersBlockToggleValidator),
ensureCanModerateUser,
asyncMiddleware(unblockUser) asyncMiddleware(unblockUser)
) )
usersRouter.get('/:id', usersRouter.get('/:id', authenticate, ensureUserHasRight(UserRight.MANAGE_USERS), asyncMiddleware(usersGetValidator), getUser)
authenticate,
ensureUserHasRight(UserRight.MANAGE_USERS),
asyncMiddleware(usersGetValidator),
getUser
)
usersRouter.post('/', usersRouter.post(
'/',
authenticate, authenticate,
ensureUserHasRight(UserRight.MANAGE_USERS), ensureUserHasRight(UserRight.MANAGE_USERS),
asyncMiddleware(usersAddValidator), asyncMiddleware(usersAddValidator),
asyncRetryTransactionMiddleware(createUser) asyncRetryTransactionMiddleware(createUser)
) )
usersRouter.put('/:id', usersRouter.put(
'/:id',
authenticate, authenticate,
ensureUserHasRight(UserRight.MANAGE_USERS), ensureUserHasRight(UserRight.MANAGE_USERS),
asyncMiddleware(usersUpdateValidator), asyncMiddleware(usersUpdateValidator),
ensureCanModerateUser,
asyncMiddleware(updateUser) asyncMiddleware(updateUser)
) )
usersRouter.delete('/:id', usersRouter.delete(
'/:id',
authenticate, authenticate,
ensureUserHasRight(UserRight.MANAGE_USERS), ensureUserHasRight(UserRight.MANAGE_USERS),
asyncMiddleware(usersRemoveValidator), asyncMiddleware(usersRemoveValidator),
ensureCanModerateUser,
asyncMiddleware(removeUser) asyncMiddleware(removeUser)
) )
usersRouter.post('/ask-reset-password', usersRouter.post('/ask-reset-password', asyncMiddleware(usersAskResetPasswordValidator), asyncMiddleware(askResetUserPassword))
asyncMiddleware(usersAskResetPasswordValidator),
asyncMiddleware(askResetUserPassword)
)
usersRouter.post('/:id/reset-password', usersRouter.post('/:id/reset-password', asyncMiddleware(usersResetPasswordValidator), asyncMiddleware(resetUserPassword))
asyncMiddleware(usersResetPasswordValidator),
asyncMiddleware(resetUserPassword)
)
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------

View file

@ -1,4 +1,4 @@
import express from 'express' import { HttpStatusCode, VideoChannelSyncState } from '@peertube/peertube-models'
import { auditLoggerFactory, getAuditIdFromRes, VideoChannelSyncAuditView } from '@server/helpers/audit-logger.js' import { auditLoggerFactory, getAuditIdFromRes, VideoChannelSyncAuditView } from '@server/helpers/audit-logger.js'
import { logger } from '@server/helpers/logger.js' import { logger } from '@server/helpers/logger.js'
import { import {
@ -6,32 +6,31 @@ import {
asyncMiddleware, asyncMiddleware,
asyncRetryTransactionMiddleware, asyncRetryTransactionMiddleware,
authenticate, authenticate,
ensureCanManageChannelOrAccount,
ensureSyncExists, ensureSyncExists,
ensureSyncIsEnabled, ensureSyncIsEnabled,
videoChannelSyncValidator videoChannelSyncValidator
} from '@server/middlewares/index.js' } from '@server/middlewares/index.js'
import { VideoChannelSyncModel } from '@server/models/video/video-channel-sync.js' import { VideoChannelSyncModel } from '@server/models/video/video-channel-sync.js'
import { MChannelSyncFormattable } from '@server/types/models/index.js' import { MChannelSyncFormattable } from '@server/types/models/index.js'
import { HttpStatusCode, VideoChannelSyncState } from '@peertube/peertube-models' import express from 'express'
const videoChannelSyncRouter = express.Router() const videoChannelSyncRouter = express.Router()
const auditLogger = auditLoggerFactory('channel-syncs') const auditLogger = auditLoggerFactory('channel-syncs')
videoChannelSyncRouter.use(apiRateLimiter) videoChannelSyncRouter.use(apiRateLimiter)
videoChannelSyncRouter.post('/', videoChannelSyncRouter.post(
'/',
authenticate, authenticate,
ensureSyncIsEnabled, ensureSyncIsEnabled,
asyncMiddleware(videoChannelSyncValidator), asyncMiddleware(videoChannelSyncValidator),
ensureCanManageChannelOrAccount,
asyncRetryTransactionMiddleware(createVideoChannelSync) asyncRetryTransactionMiddleware(createVideoChannelSync)
) )
videoChannelSyncRouter.delete('/:id', videoChannelSyncRouter.delete(
'/:id',
authenticate, authenticate,
asyncMiddleware(ensureSyncExists), asyncMiddleware(ensureSyncExists),
ensureCanManageChannelOrAccount,
asyncRetryTransactionMiddleware(removeVideoChannelSync) asyncRetryTransactionMiddleware(removeVideoChannelSync)
) )

View file

@ -1,4 +1,3 @@
import express from 'express'
import { import {
ActorImageType, ActorImageType,
HttpStatusCode, HttpStatusCode,
@ -11,6 +10,7 @@ import { Hooks } from '@server/lib/plugins/hooks.js'
import { ActorFollowModel } from '@server/models/actor/actor-follow.js' import { ActorFollowModel } from '@server/models/actor/actor-follow.js'
import { getServerActor } from '@server/models/application/application.js' import { getServerActor } from '@server/models/application/application.js'
import { MChannelBannerAccountDefault } from '@server/types/models/index.js' import { MChannelBannerAccountDefault } from '@server/types/models/index.js'
import express from 'express'
import { auditLoggerFactory, getAuditIdFromRes, VideoChannelAuditView } from '../../helpers/audit-logger.js' import { auditLoggerFactory, getAuditIdFromRes, VideoChannelAuditView } from '../../helpers/audit-logger.js'
import { resetSequelizeInstance } from '../../helpers/database-utils.js' import { resetSequelizeInstance } from '../../helpers/database-utils.js'
import { buildNSFWFilter, createReqFiles, getCountVideos, isUserAbleToSearchRemoteURI } from '../../helpers/express-utils.js' import { buildNSFWFilter, createReqFiles, getCountVideos, isUserAbleToSearchRemoteURI } from '../../helpers/express-utils.js'
@ -28,7 +28,6 @@ import {
asyncRetryTransactionMiddleware, asyncRetryTransactionMiddleware,
authenticate, authenticate,
commonVideosFiltersValidator, commonVideosFiltersValidator,
ensureCanManageChannelOrAccount,
optionalAuthenticate, optionalAuthenticate,
paginationValidator, paginationValidator,
setDefaultPagination, setDefaultPagination,
@ -43,11 +42,10 @@ import {
import { updateAvatarValidator, updateBannerValidator } from '../../middlewares/validators/actor-image.js' import { updateAvatarValidator, updateBannerValidator } from '../../middlewares/validators/actor-image.js'
import { import {
ensureChannelOwnerCanUpload, ensureChannelOwnerCanUpload,
ensureIsLocalChannel,
videoChannelImportVideosValidator, videoChannelImportVideosValidator,
videoChannelsFollowersSortValidator, videoChannelsFollowersSortValidator,
videoChannelsHandleValidatorFactory,
videoChannelsListValidator, videoChannelsListValidator,
videoChannelsNameWithHostValidator,
videosSortValidator videosSortValidator
} from '../../middlewares/validators/index.js' } from '../../middlewares/validators/index.js'
import { commonVideoPlaylistFiltersValidator } from '../../middlewares/validators/videos/video-playlists.js' import { commonVideoPlaylistFiltersValidator } from '../../middlewares/validators/videos/video-playlists.js'
@ -65,7 +63,8 @@ const videoChannelRouter = express.Router()
videoChannelRouter.use(apiRateLimiter) videoChannelRouter.use(apiRateLimiter)
videoChannelRouter.get('/', videoChannelRouter.get(
'/',
paginationValidator, paginationValidator,
videoChannelsSortValidator, videoChannelsSortValidator,
setDefaultSort, setDefaultSort,
@ -74,74 +73,66 @@ videoChannelRouter.get('/',
asyncMiddleware(listVideoChannels) asyncMiddleware(listVideoChannels)
) )
videoChannelRouter.post('/', videoChannelRouter.post('/', authenticate, asyncMiddleware(videoChannelsAddValidator), asyncRetryTransactionMiddleware(createVideoChannel))
authenticate,
asyncMiddleware(videoChannelsAddValidator),
asyncRetryTransactionMiddleware(createVideoChannel)
)
videoChannelRouter.post('/:nameWithHost/avatar/pick', videoChannelRouter.post(
'/:nameWithHost/avatar/pick',
authenticate, authenticate,
reqAvatarFile, reqAvatarFile,
asyncMiddleware(videoChannelsNameWithHostValidator), asyncMiddleware(videoChannelsHandleValidatorFactory({ checkIsLocal: true, checkManage: true })),
ensureIsLocalChannel,
ensureCanManageChannelOrAccount,
updateAvatarValidator, updateAvatarValidator,
asyncMiddleware(updateVideoChannelAvatar) asyncMiddleware(updateVideoChannelAvatar)
) )
videoChannelRouter.post('/:nameWithHost/banner/pick', videoChannelRouter.post(
'/:nameWithHost/banner/pick',
authenticate, authenticate,
reqBannerFile, reqBannerFile,
asyncMiddleware(videoChannelsNameWithHostValidator), asyncMiddleware(videoChannelsHandleValidatorFactory({ checkIsLocal: true, checkManage: true })),
ensureIsLocalChannel,
ensureCanManageChannelOrAccount,
updateBannerValidator, updateBannerValidator,
asyncMiddleware(updateVideoChannelBanner) asyncMiddleware(updateVideoChannelBanner)
) )
videoChannelRouter.delete('/:nameWithHost/avatar', videoChannelRouter.delete(
'/:nameWithHost/avatar',
authenticate, authenticate,
asyncMiddleware(videoChannelsNameWithHostValidator), asyncMiddleware(videoChannelsHandleValidatorFactory({ checkIsLocal: true, checkManage: true })),
ensureIsLocalChannel,
ensureCanManageChannelOrAccount,
asyncMiddleware(deleteVideoChannelAvatar) asyncMiddleware(deleteVideoChannelAvatar)
) )
videoChannelRouter.delete('/:nameWithHost/banner', videoChannelRouter.delete(
'/:nameWithHost/banner',
authenticate, authenticate,
asyncMiddleware(videoChannelsNameWithHostValidator), asyncMiddleware(videoChannelsHandleValidatorFactory({ checkIsLocal: true, checkManage: true })),
ensureIsLocalChannel,
ensureCanManageChannelOrAccount,
asyncMiddleware(deleteVideoChannelBanner) asyncMiddleware(deleteVideoChannelBanner)
) )
videoChannelRouter.put('/:nameWithHost', videoChannelRouter.put(
'/:nameWithHost',
authenticate, authenticate,
asyncMiddleware(videoChannelsNameWithHostValidator), asyncMiddleware(videoChannelsHandleValidatorFactory({ checkIsLocal: true, checkManage: true })),
ensureIsLocalChannel,
ensureCanManageChannelOrAccount,
videoChannelsUpdateValidator, videoChannelsUpdateValidator,
asyncRetryTransactionMiddleware(updateVideoChannel) asyncRetryTransactionMiddleware(updateVideoChannel)
) )
videoChannelRouter.delete('/:nameWithHost', videoChannelRouter.delete(
'/:nameWithHost',
authenticate, authenticate,
asyncMiddleware(videoChannelsNameWithHostValidator), asyncMiddleware(videoChannelsHandleValidatorFactory({ checkIsLocal: true, checkManage: true })),
ensureIsLocalChannel,
ensureCanManageChannelOrAccount,
asyncMiddleware(videoChannelsRemoveValidator), asyncMiddleware(videoChannelsRemoveValidator),
asyncRetryTransactionMiddleware(removeVideoChannel) asyncRetryTransactionMiddleware(removeVideoChannel)
) )
videoChannelRouter.get('/:nameWithHost', videoChannelRouter.get(
asyncMiddleware(videoChannelsNameWithHostValidator), '/:nameWithHost',
asyncMiddleware(videoChannelsHandleValidatorFactory({ checkIsLocal: false, checkManage: false })),
asyncMiddleware(getVideoChannel) asyncMiddleware(getVideoChannel)
) )
videoChannelRouter.get('/:nameWithHost/video-playlists', videoChannelRouter.get(
'/:nameWithHost/video-playlists',
optionalAuthenticate, optionalAuthenticate,
asyncMiddleware(videoChannelsNameWithHostValidator), asyncMiddleware(videoChannelsHandleValidatorFactory({ checkIsLocal: false, checkManage: false })),
paginationValidator, paginationValidator,
videoPlaylistsSortValidator, videoPlaylistsSortValidator,
setDefaultSort, setDefaultSort,
@ -150,8 +141,9 @@ videoChannelRouter.get('/:nameWithHost/video-playlists',
asyncMiddleware(listVideoChannelPlaylists) asyncMiddleware(listVideoChannelPlaylists)
) )
videoChannelRouter.get('/:nameWithHost/videos', videoChannelRouter.get(
asyncMiddleware(videoChannelsNameWithHostValidator), '/:nameWithHost/videos',
asyncMiddleware(videoChannelsHandleValidatorFactory({ checkIsLocal: false, checkManage: false })),
paginationValidator, paginationValidator,
videosSortValidator, videosSortValidator,
setDefaultVideosSort, setDefaultVideosSort,
@ -161,10 +153,10 @@ videoChannelRouter.get('/:nameWithHost/videos',
asyncMiddleware(listVideoChannelVideos) asyncMiddleware(listVideoChannelVideos)
) )
videoChannelRouter.get('/:nameWithHost/followers', videoChannelRouter.get(
'/:nameWithHost/followers',
authenticate, authenticate,
asyncMiddleware(videoChannelsNameWithHostValidator), asyncMiddleware(videoChannelsHandleValidatorFactory({ checkIsLocal: false, checkManage: true })),
ensureCanManageChannelOrAccount,
paginationValidator, paginationValidator,
videoChannelsFollowersSortValidator, videoChannelsFollowersSortValidator,
setDefaultSort, setDefaultSort,
@ -172,12 +164,11 @@ videoChannelRouter.get('/:nameWithHost/followers',
asyncMiddleware(listVideoChannelFollowers) asyncMiddleware(listVideoChannelFollowers)
) )
videoChannelRouter.post('/:nameWithHost/import-videos', videoChannelRouter.post(
'/:nameWithHost/import-videos',
authenticate, authenticate,
asyncMiddleware(videoChannelsNameWithHostValidator), asyncMiddleware(videoChannelsHandleValidatorFactory({ checkIsLocal: true, checkManage: true })),
asyncMiddleware(videoChannelImportVideosValidator), asyncMiddleware(videoChannelImportVideosValidator),
ensureIsLocalChannel,
ensureCanManageChannelOrAccount,
asyncMiddleware(ensureChannelOwnerCanUpload), asyncMiddleware(ensureChannelOwnerCanUpload),
asyncMiddleware(importVideosInChannel) asyncMiddleware(importVideosInChannel)
) )

View file

@ -4,19 +4,15 @@ import { escapeHTML, forceNumber } from '@peertube/peertube-core-utils'
import { MChannelSummary } from '@server/types/models/index.js' import { MChannelSummary } from '@server/types/models/index.js'
import { EMBED_SIZE, PREVIEWS_SIZE, THUMBNAILS_SIZE, WEBSERVER } from '../initializers/constants.js' import { EMBED_SIZE, PREVIEWS_SIZE, THUMBNAILS_SIZE, WEBSERVER } from '../initializers/constants.js'
import { apiRateLimiter, asyncMiddleware, oembedValidator } from '../middlewares/index.js' import { apiRateLimiter, asyncMiddleware, oembedValidator } from '../middlewares/index.js'
import { accountNameWithHostGetValidator } from '../middlewares/validators/index.js' import { accountHandleGetValidatorFactory } from '../middlewares/validators/index.js'
const servicesRouter = express.Router() const servicesRouter = express.Router()
servicesRouter.use('/oembed', servicesRouter.use('/oembed', cors(), apiRateLimiter, asyncMiddleware(oembedValidator), generateOEmbed)
cors(), servicesRouter.use(
'/redirect/accounts/:accountName',
apiRateLimiter, apiRateLimiter,
asyncMiddleware(oembedValidator), asyncMiddleware(accountHandleGetValidatorFactory({ checkIsLocal: false, checkManage: false })),
generateOEmbed
)
servicesRouter.use('/redirect/accounts/:accountName',
apiRateLimiter,
asyncMiddleware(accountNameWithHostGetValidator),
redirectToAccountUrl redirectToAccountUrl
) )

View file

@ -23,7 +23,7 @@ export class ActorHtml {
} }
static async getVideoChannelHTMLPage (nameWithHost: string, req: express.Request, res: express.Response) { static async getVideoChannelHTMLPage (nameWithHost: string, req: express.Request, res: express.Response) {
const videoChannel = await VideoChannelModel.loadByNameWithHostAndPopulateAccount(nameWithHost) const videoChannel = await VideoChannelModel.loadByHandleAndPopulateAccount(nameWithHost)
return this.getAccountOrChannelHTMLPage({ return this.getAccountOrChannelHTMLPage({
loader: () => Promise.resolve(videoChannel), loader: () => Promise.resolve(videoChannel),
@ -36,7 +36,7 @@ export class ActorHtml {
static async getActorHTMLPage (nameWithHost: string, req: express.Request, res: express.Response) { static async getActorHTMLPage (nameWithHost: string, req: express.Request, res: express.Response) {
const [ account, channel ] = await Promise.all([ const [ account, channel ] = await Promise.all([
AccountModel.loadByNameWithHost(nameWithHost), AccountModel.loadByNameWithHost(nameWithHost),
VideoChannelModel.loadByNameWithHostAndPopulateAccount(nameWithHost) VideoChannelModel.loadByHandleAndPopulateAccount(nameWithHost)
]) ])
return this.getAccountOrChannelHTMLPage({ return this.getAccountOrChannelHTMLPage({

View file

@ -57,7 +57,7 @@ const abuseReportValidator = [
const body: AbuseCreate = req.body const body: AbuseCreate = req.body
if (body.video?.id && !await doesVideoExist(body.video.id, res)) return if (body.video?.id && !await doesVideoExist(body.video.id, res)) return
if (body.account?.id && !await doesAccountIdExist(body.account.id, res)) return if (body.account?.id && !await doesAccountIdExist({ id: body.account.id, res, checkIsLocal: false, checkManage: false })) return
if (body.comment?.id && !await doesCommentIdExist(body.comment.id, res)) return if (body.comment?.id && !await doesCommentIdExist(body.comment.id, res)) return
if (!body.video?.id && !body.account?.id && !body.comment?.id) { if (!body.video?.id && !body.account?.id && !body.comment?.id) {

View file

@ -1,35 +1,31 @@
import { UserRight } from '@peertube/peertube-models'
import express from 'express' import express from 'express'
import { param } from 'express-validator' import { param } from 'express-validator'
import { isAccountNameValid } from '../../helpers/custom-validators/accounts.js' import { areValidationErrors, checkUserCanManageAccount, doesAccountHandleExist } from './shared/index.js'
import { areValidationErrors, doesAccountNameWithHostExist, doesLocalAccountNameExist } from './shared/index.js'
const localAccountValidator = [ export const accountHandleGetValidatorFactory = (options: {
param('name') checkManage: boolean
.custom(isAccountNameValid), checkIsLocal: boolean
}) => {
const { checkManage, checkIsLocal } = options
async (req: express.Request, res: express.Response, next: express.NextFunction) => { return [
if (areValidationErrors(req, res)) return param('accountName')
if (!await doesLocalAccountNameExist(req.params.name, res)) return .exists(),
return next() async (req: express.Request, res: express.Response, next: express.NextFunction) => {
} if (areValidationErrors(req, res)) return
] if (!await doesAccountHandleExist({ handle: req.params.accountName, res, checkIsLocal, checkManage })) return
const accountNameWithHostGetValidator = [ if (options.checkManage) {
param('accountName') const user = res.locals.oauth.token.User
.exists(),
async (req: express.Request, res: express.Response, next: express.NextFunction) => { if (!checkUserCanManageAccount({ account: res.locals.account, user, res, specialRight: UserRight.MANAGE_USERS })) {
if (areValidationErrors(req, res)) return return false
if (!await doesAccountNameWithHostExist(req.params.accountName, res)) return }
}
return next() return next()
} }
] ]
// ---------------------------------------------------------------------------
export {
localAccountValidator,
accountNameWithHostGetValidator
} }

View file

@ -3,8 +3,7 @@ import { isStringArray } from '@server/helpers/custom-validators/search.js'
import { AutomaticTagger } from '@server/lib/automatic-tags/automatic-tagger.js' import { AutomaticTagger } from '@server/lib/automatic-tags/automatic-tagger.js'
import express from 'express' import express from 'express'
import { body, param } from 'express-validator' import { body, param } from 'express-validator'
import { doesAccountNameWithHostExist } from './shared/accounts.js' import { doesAccountHandleExist } from './shared/accounts.js'
import { checkUserCanManageAccount } from './shared/users.js'
import { areValidationErrors } from './shared/utils.js' import { areValidationErrors } from './shared/utils.js'
export const manageAccountAutomaticTagsValidator = [ export const manageAccountAutomaticTagsValidator = [
@ -13,8 +12,7 @@ export const manageAccountAutomaticTagsValidator = [
async (req: express.Request, res: express.Response, next: express.NextFunction) => { async (req: express.Request, res: express.Response, next: express.NextFunction) => {
if (areValidationErrors(req, res)) return if (areValidationErrors(req, res)) return
if (!await doesAccountNameWithHostExist(req.params.accountName, res)) return if (!await doesAccountHandleExist({ handle: req.params.accountName, res, checkIsLocal: true, checkManage: true })) return
if (!checkUserCanManageAccount({ user: res.locals.oauth.token.User, account: res.locals.account, specialRight: null, res })) return
return next() return next()
} }

View file

@ -9,7 +9,7 @@ import { WEBSERVER } from '../../initializers/constants.js'
import { AccountBlocklistModel } from '../../models/account/account-blocklist.js' import { AccountBlocklistModel } from '../../models/account/account-blocklist.js'
import { ServerBlocklistModel } from '../../models/server/server-blocklist.js' import { ServerBlocklistModel } from '../../models/server/server-blocklist.js'
import { ServerModel } from '../../models/server/server.js' import { ServerModel } from '../../models/server/server.js'
import { areValidationErrors, doesAccountNameWithHostExist } from './shared/index.js' import { areValidationErrors, doesAccountHandleExist } from './shared/index.js'
const blockAccountValidator = [ const blockAccountValidator = [
body('accountName') body('accountName')
@ -17,7 +17,7 @@ const blockAccountValidator = [
async (req: express.Request, res: express.Response, next: express.NextFunction) => { async (req: express.Request, res: express.Response, next: express.NextFunction) => {
if (areValidationErrors(req, res)) return if (areValidationErrors(req, res)) return
if (!await doesAccountNameWithHostExist(req.body.accountName, res)) return if (!await doesAccountHandleExist({ handle: req.body.accountName, res, checkIsLocal: false, checkManage: false })) return
const user = res.locals.oauth.token.User const user = res.locals.oauth.token.User
const accountToBlock = res.locals.account const accountToBlock = res.locals.account
@ -40,7 +40,7 @@ const unblockAccountByAccountValidator = [
async (req: express.Request, res: express.Response, next: express.NextFunction) => { async (req: express.Request, res: express.Response, next: express.NextFunction) => {
if (areValidationErrors(req, res)) return if (areValidationErrors(req, res)) return
if (!await doesAccountNameWithHostExist(req.params.accountName, res)) return if (!await doesAccountHandleExist({ handle: req.params.accountName, res, checkIsLocal: false, checkManage: false })) return
const user = res.locals.oauth.token.User const user = res.locals.oauth.token.User
const targetAccount = res.locals.account const targetAccount = res.locals.account
@ -56,7 +56,7 @@ const unblockAccountByServerValidator = [
async (req: express.Request, res: express.Response, next: express.NextFunction) => { async (req: express.Request, res: express.Response, next: express.NextFunction) => {
if (areValidationErrors(req, res)) return if (areValidationErrors(req, res)) return
if (!await doesAccountNameWithHostExist(req.params.accountName, res)) return if (!await doesAccountHandleExist({ handle: req.params.accountName, res, checkIsLocal: false, checkManage: false })) return
const serverActor = await getServerActor() const serverActor = await getServerActor()
const targetAccount = res.locals.account const targetAccount = res.locals.account

View file

@ -1,10 +1,10 @@
import express from 'express'
import { body } from 'express-validator'
import { BulkRemoveCommentsOfBody, HttpStatusCode, UserRight } from '@peertube/peertube-models' import { BulkRemoveCommentsOfBody, HttpStatusCode, UserRight } from '@peertube/peertube-models'
import { isBulkRemoveCommentsOfScopeValid } from '@server/helpers/custom-validators/bulk.js' import { isBulkRemoveCommentsOfScopeValid } from '@server/helpers/custom-validators/bulk.js'
import { areValidationErrors, doesAccountNameWithHostExist } from './shared/index.js' import express from 'express'
import { body } from 'express-validator'
import { areValidationErrors, doesAccountHandleExist } from './shared/index.js'
const bulkRemoveCommentsOfValidator = [ export const bulkRemoveCommentsOfValidator = [
body('accountName') body('accountName')
.exists(), .exists(),
body('scope') body('scope')
@ -12,7 +12,7 @@ const bulkRemoveCommentsOfValidator = [
async (req: express.Request, res: express.Response, next: express.NextFunction) => { async (req: express.Request, res: express.Response, next: express.NextFunction) => {
if (areValidationErrors(req, res)) return if (areValidationErrors(req, res)) return
if (!await doesAccountNameWithHostExist(req.body.accountName, res)) return if (!await doesAccountHandleExist({ handle: req.body.accountName, res, checkIsLocal: false, checkManage: false })) return
const user = res.locals.oauth.token.User const user = res.locals.oauth.token.User
const body = req.body as BulkRemoveCommentsOfBody const body = req.body as BulkRemoveCommentsOfBody
@ -27,11 +27,3 @@ const bulkRemoveCommentsOfValidator = [
return next() return next()
} }
] ]
// ---------------------------------------------------------------------------
export {
bulkRemoveCommentsOfValidator
}
// ---------------------------------------------------------------------------

View file

@ -1,16 +1,16 @@
import { HttpStatusCode } from '@peertube/peertube-models'
import express from 'express' import express from 'express'
import { param, query } from 'express-validator' import { param, query } from 'express-validator'
import { HttpStatusCode } from '@peertube/peertube-models'
import { isValidRSSFeed } from '../../helpers/custom-validators/feeds.js' import { isValidRSSFeed } from '../../helpers/custom-validators/feeds.js'
import { exists, isIdOrUUIDValid, isIdValid, toCompleteUUID } from '../../helpers/custom-validators/misc.js' import { exists, isIdOrUUIDValid, isIdValid, toCompleteUUID } from '../../helpers/custom-validators/misc.js'
import { import {
areValidationErrors, areValidationErrors,
checkCanSeeVideo, checkCanSeeVideo,
doesAccountHandleExist,
doesAccountIdExist, doesAccountIdExist,
doesAccountNameWithHostExist, doesChannelHandleExist,
doesChannelIdExist,
doesUserFeedTokenCorrespond, doesUserFeedTokenCorrespond,
doesVideoChannelIdExist,
doesVideoChannelNameWithHostExist,
doesVideoExist doesVideoExist
} from './shared/index.js' } from './shared/index.js'
@ -90,10 +90,14 @@ const feedsAccountOrChannelFiltersValidator = [
async (req: express.Request, res: express.Response, next: express.NextFunction) => { async (req: express.Request, res: express.Response, next: express.NextFunction) => {
if (areValidationErrors(req, res)) return if (areValidationErrors(req, res)) return
if (req.query.accountId && !await doesAccountIdExist(req.query.accountId, res)) return const { accountId, videoChannelId, accountName, videoChannelName } = req.query
if (req.query.videoChannelId && !await doesVideoChannelIdExist(req.query.videoChannelId, res)) return const commonOptions = { res, checkManage: false, checkIsLocal: false }
if (req.query.accountName && !await doesAccountNameWithHostExist(req.query.accountName, res)) return
if (req.query.videoChannelName && !await doesVideoChannelNameWithHostExist(req.query.videoChannelName, res)) return if (accountId && !await doesAccountIdExist({ id: accountId, ...commonOptions })) return
if (videoChannelId && !await doesChannelIdExist({ id: videoChannelId, ...commonOptions })) return
if (accountName && !await doesAccountHandleExist({ handle: accountName, ...commonOptions })) return
if (videoChannelName && !await doesChannelHandleExist({ handle: videoChannelName, ...commonOptions })) return
return next() return next()
} }
@ -107,7 +111,7 @@ const videoFeedsPodcastValidator = [
async (req: express.Request, res: express.Response, next: express.NextFunction) => { async (req: express.Request, res: express.Response, next: express.NextFunction) => {
if (areValidationErrors(req, res)) return if (areValidationErrors(req, res)) return
if (!await doesVideoChannelIdExist(req.query.videoChannelId, res)) return if (!await doesChannelIdExist({ id: req.query.videoChannelId, checkManage: false, checkIsLocal: false, res })) return
return next() return next()
} }
@ -125,7 +129,7 @@ const videoSubscriptionFeedsValidator = [
async (req: express.Request, res: express.Response, next: express.NextFunction) => { async (req: express.Request, res: express.Response, next: express.NextFunction) => {
if (areValidationErrors(req, res)) return if (areValidationErrors(req, res)) return
if (!await doesAccountIdExist(req.query.accountId, res)) return if (!await doesAccountIdExist({ id: req.query.accountId, res, checkIsLocal: true, checkManage: false })) return
if (!await doesUserFeedTokenCorrespond(res.locals.account.userId, req.query.token, res)) return if (!await doesUserFeedTokenCorrespond(res.locals.account.userId, req.query.token, res)) return
return next() return next()
@ -157,11 +161,11 @@ const videoCommentsFeedsValidator = [
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
export { export {
feedsAccountOrChannelFiltersValidator,
feedsFormatValidator, feedsFormatValidator,
setFeedFormatContentType, setFeedFormatContentType,
setFeedPodcastContentType, setFeedPodcastContentType,
feedsAccountOrChannelFiltersValidator, videoCommentsFeedsValidator,
videoFeedsPodcastValidator, videoFeedsPodcastValidator,
videoSubscriptionFeedsValidator, videoSubscriptionFeedsValidator
videoCommentsFeedsValidator
} }

View file

@ -1,66 +1,73 @@
import { Response } from 'express'
import { AccountModel } from '@server/models/account/account.js'
import { UserModel } from '@server/models/user/user.js'
import { MAccountDefault } from '@server/types/models/index.js'
import { forceNumber } from '@peertube/peertube-core-utils' import { forceNumber } from '@peertube/peertube-core-utils'
import { HttpStatusCode } from '@peertube/peertube-models' import { HttpStatusCode, UserRight } from '@peertube/peertube-models'
import { AccountModel } from '@server/models/account/account.js'
import { MAccountDefault } from '@server/types/models/index.js'
import { Response } from 'express'
import { checkUserCanManageAccount } from './users.js'
function doesAccountIdExist (id: number | string, res: Response, sendNotFound = true) { export async function doesAccountIdExist (options: {
const promise = AccountModel.load(forceNumber(id)) id: string | number
res: Response
checkManage: boolean // Also check the user can manage the account
checkIsLocal: boolean // Also check this is a local channel
}) {
const { id, res, checkIsLocal, checkManage } = options
return doesAccountExist(promise, res, sendNotFound) const account = await AccountModel.load(forceNumber(id))
return doesAccountExist({ account, res, checkIsLocal, checkManage })
} }
function doesLocalAccountNameExist (name: string, res: Response, sendNotFound = true) { export async function doesAccountHandleExist (options: {
const promise = AccountModel.loadLocalByName(name) handle: string
res: Response
checkManage: boolean // Also check the user can manage the account
checkIsLocal: boolean // Also check this is a local channel
}) {
const { handle, res, checkIsLocal, checkManage } = options
return doesAccountExist(promise, res, sendNotFound) const account = await AccountModel.loadByNameWithHost(handle)
return doesAccountExist({ account, res, checkIsLocal, checkManage })
} }
function doesAccountNameWithHostExist (nameWithDomain: string, res: Response, sendNotFound = true) { // ---------------------------------------------------------------------------
const promise = AccountModel.loadByNameWithHost(nameWithDomain) // Private
// ---------------------------------------------------------------------------
return doesAccountExist(promise, res, sendNotFound) function doesAccountExist (options: {
} account: MAccountDefault
res: Response
async function doesAccountExist (p: Promise<MAccountDefault>, res: Response, sendNotFound: boolean) { checkManage: boolean
const account = await p checkIsLocal: boolean
}) {
const { account, res, checkIsLocal, checkManage } = options
if (!account) { if (!account) {
if (sendNotFound === true) { res.fail({
res.fail({ status: HttpStatusCode.NOT_FOUND_404,
status: HttpStatusCode.NOT_FOUND_404, message: 'Account not found'
message: 'Account not found' })
}) return false
}
if (checkManage) {
const user = res.locals.oauth.token.User
if (!checkUserCanManageAccount({ account, user, res, specialRight: UserRight.MANAGE_USERS })) {
return false
} }
}
if (checkIsLocal && account.Actor.isOwned() === false) {
res.fail({
status: HttpStatusCode.FORBIDDEN_403,
message: 'This account is not owned.'
})
return false return false
} }
res.locals.account = account res.locals.account = account
return true return true
} }
async function doesUserFeedTokenCorrespond (id: number, token: string, res: Response) {
const user = await UserModel.loadByIdWithChannels(forceNumber(id))
if (token !== user.feedToken) {
res.fail({
status: HttpStatusCode.FORBIDDEN_403,
message: 'User and token mismatch'
})
return false
}
res.locals.user = user
return true
}
// ---------------------------------------------------------------------------
export {
doesAccountIdExist,
doesLocalAccountNameExist,
doesAccountNameWithHostExist,
doesAccountExist,
doesUserFeedTokenCorrespond
}

View file

@ -12,11 +12,15 @@ export function checkUserIdExist (idArg: number | string, res: express.Response,
} }
export function checkUserEmailExistPermissive (email: string, res: express.Response, abortResponse = true) { export function checkUserEmailExistPermissive (email: string, res: express.Response, abortResponse = true) {
return checkUserExist(async () => { return checkUserExist(
const users = await UserModel.loadByEmailCaseInsensitive(email) async () => {
const users = await UserModel.loadByEmailCaseInsensitive(email)
return getUserByEmailPermissive(users, email) return getUserByEmailPermissive(users, email)
}, res, abortResponse) },
res,
abortResponse
)
} }
export async function checkUsernameOrEmailDoNotAlreadyExist (username: string, email: string, res: express.Response) { export async function checkUsernameOrEmailDoNotAlreadyExist (username: string, email: string, res: express.Response) {
@ -102,3 +106,18 @@ export function checkUserCanManageAccount (options: {
return false return false
} }
export async function doesUserFeedTokenCorrespond (id: number, token: string, res: express.Response) {
const user = await UserModel.loadByIdWithChannels(forceNumber(id))
if (token !== user.feedToken) {
res.fail({
status: HttpStatusCode.FORBIDDEN_403,
message: 'User and token mismatch'
})
return false
}
res.locals.user = user
return true
}

View file

@ -1,29 +1,48 @@
import express from 'express' import { HttpStatusCode, UserRight } from '@peertube/peertube-models'
import { VideoChannelModel } from '@server/models/video/video-channel.js' import { VideoChannelModel } from '@server/models/video/video-channel.js'
import { MChannelBannerAccountDefault } from '@server/types/models/index.js' import { MChannelBannerAccountDefault } from '@server/types/models/index.js'
import { HttpStatusCode } from '@peertube/peertube-models' import express from 'express'
import { checkUserCanManageAccount } from './users.js'
async function doesVideoChannelIdExist (id: number, res: express.Response) { export async function doesChannelIdExist (options: {
const videoChannel = await VideoChannelModel.loadAndPopulateAccount(+id) id: number
checkManage: boolean // Also check the user can manage the account
checkIsLocal: boolean // Also check this is a local channel
res: express.Response
}) {
const { id, checkManage, checkIsLocal, res } = options
return processVideoChannelExist(videoChannel, res) const channel = await VideoChannelModel.loadAndPopulateAccount(+id)
return processVideoChannelExist({ channel, checkManage, checkIsLocal, res })
} }
async function doesVideoChannelNameWithHostExist (nameWithDomain: string, res: express.Response) { export async function doesChannelHandleExist (options: {
const videoChannel = await VideoChannelModel.loadByNameWithHostAndPopulateAccount(nameWithDomain) handle: string
checkManage: boolean // Also check the user can manage the account
checkIsLocal: boolean // Also check this is a local channel
res: express.Response
}) {
const { handle, checkManage, checkIsLocal, res } = options
return processVideoChannelExist(videoChannel, res) const channel = await VideoChannelModel.loadByHandleAndPopulateAccount(handle)
return processVideoChannelExist({ channel, checkManage, checkIsLocal, res })
} }
// ---------------------------------------------------------------------------
// Private
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
export { function processVideoChannelExist (options: {
doesVideoChannelIdExist, channel: MChannelBannerAccountDefault
doesVideoChannelNameWithHostExist res: express.Response
} checkManage: boolean
checkIsLocal: boolean
}) {
const { channel, res, checkManage, checkIsLocal } = options
function processVideoChannelExist (videoChannel: MChannelBannerAccountDefault, res: express.Response) { if (!channel) {
if (!videoChannel) {
res.fail({ res.fail({
status: HttpStatusCode.NOT_FOUND_404, status: HttpStatusCode.NOT_FOUND_404,
message: 'Video channel not found' message: 'Video channel not found'
@ -31,6 +50,23 @@ function processVideoChannelExist (videoChannel: MChannelBannerAccountDefault, r
return false return false
} }
res.locals.videoChannel = videoChannel if (checkManage) {
const user = res.locals.oauth.token.User
if (!checkUserCanManageAccount({ account: channel.Account, user, res, specialRight: UserRight.MANAGE_ANY_VIDEO_CHANNEL })) {
return false
}
}
if (checkIsLocal && channel.Actor.isOwned() === false) {
res.fail({
status: HttpStatusCode.FORBIDDEN_403,
message: 'This channel is not owned.'
})
return false
}
res.locals.videoChannel = channel
return true return true
} }

View file

@ -1,7 +1,8 @@
import { arrayify, forceNumber } from '@peertube/peertube-core-utils' import { arrayify, forceNumber } from '@peertube/peertube-core-utils'
import { HttpStatusCode, ServerErrorCode, UserRight, UserRole } from '@peertube/peertube-models' import { HttpStatusCode, ServerErrorCode, UserRole } from '@peertube/peertube-models'
import { isStringArray } from '@server/helpers/custom-validators/search.js' import { isStringArray } from '@server/helpers/custom-validators/search.js'
import { Hooks } from '@server/lib/plugins/hooks.js' import { Hooks } from '@server/lib/plugins/hooks.js'
import { MUser } from '@server/types/models/user/user.js'
import express from 'express' import express from 'express'
import { body, param, query } from 'express-validator' import { body, param, query } from 'express-validator'
import { exists, isIdValid, toBooleanOrNull, toIntOrNull } from '../../../helpers/custom-validators/misc.js' import { exists, isIdValid, toBooleanOrNull, toIntOrNull } from '../../../helpers/custom-validators/misc.js'
@ -34,11 +35,10 @@ import { ActorModel } from '../../../models/actor/actor.js'
import { import {
areValidationErrors, areValidationErrors,
checkEmailDoesNotAlreadyExist, checkEmailDoesNotAlreadyExist,
checkUserCanManageAccount,
checkUserEmailExistPermissive, checkUserEmailExistPermissive,
checkUserIdExist, checkUserIdExist,
checkUsernameOrEmailDoNotAlreadyExist, checkUsernameOrEmailDoNotAlreadyExist,
doesVideoChannelIdExist, doesChannelIdExist,
doesVideoExist, doesVideoExist,
isValidVideoIdParam isValidVideoIdParam
} from '../shared/index.js' } from '../shared/index.js'
@ -128,11 +128,13 @@ export const usersRemoveValidator = [
return res.fail({ message: 'Cannot remove the root user' }) return res.fail({ message: 'Cannot remove the root user' })
} }
if (!checkUserCanModerate(user, res)) return
return next() return next()
} }
] ]
export const usersBlockingValidator = [ export const usersBlockToggleValidator = [
param('id') param('id')
.custom(isIdValid), .custom(isIdValid),
body('reason') body('reason')
@ -148,6 +150,8 @@ export const usersBlockingValidator = [
return res.fail({ message: 'Cannot block the root user' }) return res.fail({ message: 'Cannot block the root user' })
} }
if (!checkUserCanModerate(user, res)) return
return next() return next()
} }
] ]
@ -201,6 +205,8 @@ export const usersUpdateValidator = [
return res.fail({ message: 'Cannot change root role.' }) return res.fail({ message: 'Cannot change root role.' })
} }
if (!checkUserCanModerate(user, res)) return
if (req.body.email && req.body.email !== user.email && !await checkEmailDoesNotAlreadyExist(req.body.email, res)) return if (req.body.email && req.body.email !== user.email && !await checkEmailDoesNotAlreadyExist(req.body.email, res)) return
return next() return next()
@ -328,7 +334,7 @@ export const usersVideosValidator = [
async (req: express.Request, res: express.Response, next: express.NextFunction) => { async (req: express.Request, res: express.Response, next: express.NextFunction) => {
if (areValidationErrors(req, res)) return if (areValidationErrors(req, res)) return
if (req.query.channelId && !await doesVideoChannelIdExist(req.query.channelId, res)) return if (req.query.channelId && !await doesChannelIdExist({ id: req.query.channelId, checkManage: true, checkIsLocal: true, res })) return
return next() return next()
} }
@ -431,38 +437,20 @@ export const userAutocompleteValidator = [
.not().isEmpty() .not().isEmpty()
] ]
export const ensureAuthUserOwnsAccountValidator = [ // ---------------------------------------------------------------------------
(req: express.Request, res: express.Response, next: express.NextFunction) => { // Private
const user = res.locals.oauth.token.User // ---------------------------------------------------------------------------
if (!checkUserCanManageAccount({ user, account: res.locals.account, specialRight: null, res })) return function checkUserCanModerate (onUser: MUser, res: express.Response) {
const authUser = res.locals.oauth.token.User
return next() if (authUser.role === UserRole.ADMINISTRATOR) return true
} if (authUser.role === UserRole.MODERATOR && onUser.role === UserRole.USER) return true
]
export const ensureCanManageChannelOrAccount = [ res.fail({
(req: express.Request, res: express.Response, next: express.NextFunction) => { status: HttpStatusCode.FORBIDDEN_403,
const user = res.locals.oauth.token.user message: 'Users can only be managed by moderators or admins.'
const account = res.locals.videoChannel?.Account ?? res.locals.account })
if (!checkUserCanManageAccount({ account, user, res, specialRight: UserRight.MANAGE_ANY_VIDEO_CHANNEL })) return return false
}
return next()
}
]
export const ensureCanModerateUser = [
(req: express.Request, res: express.Response, next: express.NextFunction) => {
const authUser = res.locals.oauth.token.User
const onUser = res.locals.user
if (authUser.role === UserRole.ADMINISTRATOR) return next()
if (authUser.role === UserRole.MODERATOR && onUser.role === UserRole.USER) return next()
return res.fail({
status: HttpStatusCode.FORBIDDEN_403,
message: 'Users can only be managed by moderators or admins.'
})
}
]

View file

@ -4,7 +4,7 @@ import { isUrlValid } from '@server/helpers/custom-validators/activitypub/misc.j
import { CONFIG } from '@server/initializers/config.js' import { CONFIG } from '@server/initializers/config.js'
import { VideoChannelSyncModel } from '@server/models/video/video-channel-sync.js' import { VideoChannelSyncModel } from '@server/models/video/video-channel-sync.js'
import { HttpStatusCode, VideoChannelSyncCreate } from '@peertube/peertube-models' import { HttpStatusCode, VideoChannelSyncCreate } from '@peertube/peertube-models'
import { areValidationErrors, doesVideoChannelIdExist } from '../shared/index.js' import { areValidationErrors, doesChannelIdExist } from '../shared/index.js'
import { doesVideoChannelSyncIdExist } from '../shared/video-channel-syncs.js' import { doesVideoChannelSyncIdExist } from '../shared/video-channel-syncs.js'
export const ensureSyncIsEnabled = (req: express.Request, res: express.Response, next: express.NextFunction) => { export const ensureSyncIsEnabled = (req: express.Request, res: express.Response, next: express.NextFunction) => {
@ -29,7 +29,7 @@ export const videoChannelSyncValidator = [
if (areValidationErrors(req, res)) return if (areValidationErrors(req, res)) return
const body: VideoChannelSyncCreate = req.body const body: VideoChannelSyncCreate = req.body
if (!await doesVideoChannelIdExist(body.videoChannelId, res)) return if (!await doesChannelIdExist({ id: body.videoChannelId, checkManage: true, checkIsLocal: true, res })) return
const count = await VideoChannelSyncModel.countByAccount(res.locals.videoChannel.accountId) const count = await VideoChannelSyncModel.countByAccount(res.locals.videoChannel.accountId)
if (count >= CONFIG.IMPORT.VIDEO_CHANNEL_SYNCHRONIZATION.MAX_PER_USER) { if (count >= CONFIG.IMPORT.VIDEO_CHANNEL_SYNCHRONIZATION.MAX_PER_USER) {
@ -49,7 +49,7 @@ export const ensureSyncExists = [
if (areValidationErrors(req, res)) return if (areValidationErrors(req, res)) return
if (!await doesVideoChannelSyncIdExist(+req.params.id, res)) return if (!await doesVideoChannelSyncIdExist(+req.params.id, res)) return
if (!await doesVideoChannelIdExist(res.locals.videoChannelSync.videoChannelId, res)) return if (!await doesChannelIdExist({ id: res.locals.videoChannelSync.videoChannelId, checkManage: true, checkIsLocal: true, res })) return
return next() return next()
} }

View file

@ -1,9 +1,9 @@
import express from 'express'
import { body, param, query } from 'express-validator'
import { HttpStatusCode, VideosImportInChannelCreate } from '@peertube/peertube-models' import { HttpStatusCode, VideosImportInChannelCreate } from '@peertube/peertube-models'
import { isUrlValid } from '@server/helpers/custom-validators/activitypub/misc.js' import { isUrlValid } from '@server/helpers/custom-validators/activitypub/misc.js'
import { CONFIG } from '@server/initializers/config.js' import { CONFIG } from '@server/initializers/config.js'
import { MChannelAccountDefault } from '@server/types/models/index.js' import { MChannelAccountDefault } from '@server/types/models/index.js'
import express from 'express'
import { body, param, query } from 'express-validator'
import { isBooleanValid, isIdValid, toBooleanOrNull } from '../../../helpers/custom-validators/misc.js' import { isBooleanValid, isIdValid, toBooleanOrNull } from '../../../helpers/custom-validators/misc.js'
import { import {
isVideoChannelDescriptionValid, isVideoChannelDescriptionValid,
@ -13,7 +13,7 @@ import {
} from '../../../helpers/custom-validators/video-channels.js' } from '../../../helpers/custom-validators/video-channels.js'
import { ActorModel } from '../../../models/actor/actor.js' import { ActorModel } from '../../../models/actor/actor.js'
import { VideoChannelModel } from '../../../models/video/video-channel.js' import { VideoChannelModel } from '../../../models/video/video-channel.js'
import { areValidationErrors, checkUserQuota, doesVideoChannelNameWithHostExist } from '../shared/index.js' import { areValidationErrors, checkUserQuota, doesChannelHandleExist } from '../shared/index.js'
import { doesVideoChannelSyncIdExist } from '../shared/video-channel-syncs.js' import { doesVideoChannelSyncIdExist } from '../shared/video-channel-syncs.js'
export const videoChannelsAddValidator = [ export const videoChannelsAddValidator = [
@ -82,31 +82,25 @@ export const videoChannelsRemoveValidator = [
} }
] ]
export const videoChannelsNameWithHostValidator = [ export const videoChannelsHandleValidatorFactory = (options: {
param('nameWithHost') checkIsLocal: boolean
.exists(), checkManage: boolean
}) => {
const { checkIsLocal, checkManage } = options
async (req: express.Request, res: express.Response, next: express.NextFunction) => { return [
if (areValidationErrors(req, res)) return param('nameWithHost')
.exists(),
if (!await doesVideoChannelNameWithHostExist(req.params.nameWithHost, res)) return async (req: express.Request, res: express.Response, next: express.NextFunction) => {
if (areValidationErrors(req, res)) return
return next() if (!await doesChannelHandleExist({ handle: req.params.nameWithHost, checkManage, checkIsLocal, res })) return
}
]
export const ensureIsLocalChannel = [ return next()
(req: express.Request, res: express.Response, next: express.NextFunction) => {
if (res.locals.videoChannel.Actor.isOwned() === false) {
return res.fail({
status: HttpStatusCode.FORBIDDEN_403,
message: 'This channel is not owned.'
})
} }
]
return next() }
}
]
export const ensureChannelOwnerCanUpload = [ export const ensureChannelOwnerCanUpload = [
async (req: express.Request, res: express.Response, next: express.NextFunction) => { async (req: express.Request, res: express.Response, next: express.NextFunction) => {

View file

@ -24,7 +24,7 @@ import {
checkCanSeeVideo, checkCanSeeVideo,
checkUserCanManageAccount, checkUserCanManageAccount,
checkUserCanManageVideo, checkUserCanManageVideo,
doesVideoChannelIdExist, doesChannelIdExist,
doesVideoCommentExist, doesVideoCommentExist,
doesVideoCommentThreadExist, doesVideoCommentThreadExist,
doesVideoExist, doesVideoExist,
@ -51,7 +51,9 @@ export const listAllVideoCommentsForAdminValidator = [
if (areValidationErrors(req, res)) return if (areValidationErrors(req, res)) return
if (req.query.videoId && !await doesVideoExist(req.query.videoId, res, 'unsafe-only-immutable-attributes')) return if (req.query.videoId && !await doesVideoExist(req.query.videoId, res, 'unsafe-only-immutable-attributes')) return
if (req.query.videoChannelId && !await doesVideoChannelIdExist(req.query.videoChannelId, res)) return if (
req.query.videoChannelId && !await doesChannelIdExist({ id: req.query.videoChannelId, checkManage: true, checkIsLocal: true, res })
) return
return next() return next()
} }
@ -70,7 +72,9 @@ export const listCommentsOnUserVideosValidator = [
if (areValidationErrors(req, res)) return if (areValidationErrors(req, res)) return
if (req.query.videoId && !await doesVideoExist(req.query.videoId, res, 'all')) return if (req.query.videoId && !await doesVideoExist(req.query.videoId, res, 'all')) return
if (req.query.videoChannelId && !await doesVideoChannelIdExist(req.query.videoChannelId, res)) return if (
req.query.videoChannelId && !await doesChannelIdExist({ id: req.query.videoChannelId, checkManage: true, checkIsLocal: true, res })
) return
const user = res.locals.oauth.token.User const user = res.locals.oauth.token.User

View file

@ -7,8 +7,7 @@ import { WatchedWordsListModel } from '@server/models/watched-words/watched-word
import { MAccountId, MWatchedWordsList } from '@server/types/models/index.js' import { MAccountId, MWatchedWordsList } from '@server/types/models/index.js'
import express from 'express' import express from 'express'
import { ValidationChain, body, param } from 'express-validator' import { ValidationChain, body, param } from 'express-validator'
import { doesAccountNameWithHostExist } from './shared/accounts.js' import { doesAccountHandleExist } from './shared/accounts.js'
import { checkUserCanManageAccount } from './shared/users.js'
import { areValidationErrors } from './shared/utils.js' import { areValidationErrors } from './shared/utils.js'
export const manageAccountWatchedWordsListValidator = [ export const manageAccountWatchedWordsListValidator = [
@ -17,8 +16,7 @@ export const manageAccountWatchedWordsListValidator = [
async (req: express.Request, res: express.Response, next: express.NextFunction) => { async (req: express.Request, res: express.Response, next: express.NextFunction) => {
if (areValidationErrors(req, res)) return if (areValidationErrors(req, res)) return
if (!await doesAccountNameWithHostExist(req.params.accountName, res)) return if (!await doesAccountHandleExist({ handle: req.params.accountName, res, checkIsLocal: true, checkManage: true })) return
if (!checkUserCanManageAccount({ user: res.locals.oauth.token.User, account: res.locals.account, specialRight: null, res })) return
return next() return next()
} }
@ -59,14 +57,14 @@ function buildUpdateOrAddValidators ({ optional }: { optional: boolean }) {
.trim() .trim()
.custom(isWatchedWordListNameValid).withMessage( .custom(isWatchedWordListNameValid).withMessage(
`Should have a list name between ` + `Should have a list name between ` +
`${CONSTRAINTS_FIELDS.WATCHED_WORDS.LIST_NAME.min} and ${CONSTRAINTS_FIELDS.WATCHED_WORDS.LIST_NAME.max} characters long` `${CONSTRAINTS_FIELDS.WATCHED_WORDS.LIST_NAME.min} and ${CONSTRAINTS_FIELDS.WATCHED_WORDS.LIST_NAME.max} characters long`
), ),
makeOptionalIfNeeded(body('words')) makeOptionalIfNeeded(body('words'))
.custom(areWatchedWordsValid) .custom(areWatchedWordsValid)
.withMessage( .withMessage(
`Should have an array of up to ${CONSTRAINTS_FIELDS.WATCHED_WORDS.WORDS.max} words between ` + `Should have an array of up to ${CONSTRAINTS_FIELDS.WATCHED_WORDS.WORDS.max} words between ` +
`${CONSTRAINTS_FIELDS.WATCHED_WORDS.WORD.min} and ${CONSTRAINTS_FIELDS.WATCHED_WORDS.WORD.max} characters each` `${CONSTRAINTS_FIELDS.WATCHED_WORDS.WORD.min} and ${CONSTRAINTS_FIELDS.WATCHED_WORDS.WORD.max} characters each`
) )
] ]
} }

View file

@ -725,8 +725,8 @@ export class VideoChannelModel extends SequelizeModel<VideoChannelModel> {
.findOne(query) .findOne(query)
} }
static loadByNameWithHostAndPopulateAccount (nameWithHost: string) { static loadByHandleAndPopulateAccount (handle: string) {
const [ name, host ] = nameWithHost.split('@') const [ name, host ] = handle.split('@')
if (!host || host === WEBSERVER.HOST) return VideoChannelModel.loadLocalByNameAndPopulateAccount(name) if (!host || host === WEBSERVER.HOST) return VideoChannelModel.loadLocalByNameAndPopulateAccount(name)