mirror of
https://github.com/Chocobozzz/PeerTube.git
synced 2025-10-04 02:09:37 +02:00
Continue activitypub
This commit is contained in:
parent
e4f97babf7
commit
0d0e8dd090
27 changed files with 1039 additions and 1086 deletions
34
server/helpers/custom-validators/activitypub/activity.ts
Normal file
34
server/helpers/custom-validators/activitypub/activity.ts
Normal file
|
@ -0,0 +1,34 @@
|
|||
import * as validator from 'validator'
|
||||
import {
|
||||
isVideoChannelCreateActivityValid,
|
||||
isVideoTorrentAddActivityValid,
|
||||
isVideoTorrentUpdateActivityValid,
|
||||
isVideoChannelUpdateActivityValid
|
||||
} from './videos'
|
||||
|
||||
function isRootActivityValid (activity: any) {
|
||||
return Array.isArray(activity['@context']) &&
|
||||
(
|
||||
(activity.type === 'Collection' || activity.type === 'OrderedCollection') &&
|
||||
validator.isInt(activity.totalItems, { min: 0 }) &&
|
||||
Array.isArray(activity.items)
|
||||
) ||
|
||||
(
|
||||
validator.isURL(activity.id) &&
|
||||
validator.isURL(activity.actor)
|
||||
)
|
||||
}
|
||||
|
||||
function isActivityValid (activity: any) {
|
||||
return isVideoTorrentAddActivityValid(activity) ||
|
||||
isVideoChannelCreateActivityValid(activity) ||
|
||||
isVideoTorrentUpdateActivityValid(activity) ||
|
||||
isVideoChannelUpdateActivityValid(activity)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export {
|
||||
isRootActivityValid,
|
||||
isActivityValid
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
export * from './account'
|
||||
export * from './activity'
|
||||
export * from './signature'
|
||||
export * from './misc'
|
||||
export * from './videos'
|
||||
|
|
|
@ -12,6 +12,16 @@ function isActivityPubUrlValid (url: string) {
|
|||
return exists(url) && validator.isURL(url, isURLOptions)
|
||||
}
|
||||
|
||||
export {
|
||||
isActivityPubUrlValid
|
||||
function isBaseActivityValid (activity: any, type: string) {
|
||||
return Array.isArray(activity['@context']) &&
|
||||
activity.type === type &&
|
||||
validator.isURL(activity.id) &&
|
||||
validator.isURL(activity.actor) &&
|
||||
Array.isArray(activity.to) &&
|
||||
activity.to.every(t => validator.isURL(t))
|
||||
}
|
||||
|
||||
export {
|
||||
isActivityPubUrlValid,
|
||||
isBaseActivityValid
|
||||
}
|
||||
|
|
|
@ -1,184 +1,117 @@
|
|||
import 'express-validator'
|
||||
import { has, values } from 'lodash'
|
||||
import * as validator from 'validator'
|
||||
|
||||
import {
|
||||
REQUEST_ENDPOINTS,
|
||||
REQUEST_ENDPOINT_ACTIONS,
|
||||
REQUEST_VIDEO_EVENT_TYPES
|
||||
ACTIVITY_PUB
|
||||
} from '../../../initializers'
|
||||
import { isArray, isDateValid, isUUIDValid } from '../misc'
|
||||
import { isDateValid, isUUIDValid } from '../misc'
|
||||
import {
|
||||
isVideoThumbnailDataValid,
|
||||
isVideoAbuseReasonValid,
|
||||
isVideoAbuseReporterUsernameValid,
|
||||
isVideoViewsValid,
|
||||
isVideoLikesValid,
|
||||
isVideoDislikesValid,
|
||||
isVideoEventCountValid,
|
||||
isRemoteVideoCategoryValid,
|
||||
isRemoteVideoLicenceValid,
|
||||
isRemoteVideoLanguageValid,
|
||||
isVideoNSFWValid,
|
||||
isVideoTruncatedDescriptionValid,
|
||||
isVideoDurationValid,
|
||||
isVideoFileInfoHashValid,
|
||||
isVideoNameValid,
|
||||
isVideoTagsValid,
|
||||
isVideoFileExtnameValid,
|
||||
isVideoFileResolutionValid
|
||||
isVideoTagValid
|
||||
} from '../videos'
|
||||
import { isVideoChannelDescriptionValid, isVideoChannelNameValid } from '../video-channels'
|
||||
import { isVideoAuthorNameValid } from '../video-authors'
|
||||
import { isBaseActivityValid } from './misc'
|
||||
|
||||
const ENDPOINT_ACTIONS = REQUEST_ENDPOINT_ACTIONS[REQUEST_ENDPOINTS.VIDEOS]
|
||||
|
||||
const checkers: { [ id: string ]: (obj: any) => boolean } = {}
|
||||
checkers[ENDPOINT_ACTIONS.ADD_VIDEO] = checkAddVideo
|
||||
checkers[ENDPOINT_ACTIONS.UPDATE_VIDEO] = checkUpdateVideo
|
||||
checkers[ENDPOINT_ACTIONS.REMOVE_VIDEO] = checkRemoveVideo
|
||||
checkers[ENDPOINT_ACTIONS.REPORT_ABUSE] = checkReportVideo
|
||||
checkers[ENDPOINT_ACTIONS.ADD_CHANNEL] = checkAddVideoChannel
|
||||
checkers[ENDPOINT_ACTIONS.UPDATE_CHANNEL] = checkUpdateVideoChannel
|
||||
checkers[ENDPOINT_ACTIONS.REMOVE_CHANNEL] = checkRemoveVideoChannel
|
||||
checkers[ENDPOINT_ACTIONS.ADD_AUTHOR] = checkAddAuthor
|
||||
checkers[ENDPOINT_ACTIONS.REMOVE_AUTHOR] = checkRemoveAuthor
|
||||
|
||||
function removeBadRequestVideos (requests: any[]) {
|
||||
for (let i = requests.length - 1; i >= 0 ; i--) {
|
||||
const request = requests[i]
|
||||
const video = request.data
|
||||
|
||||
if (
|
||||
!video ||
|
||||
checkers[request.type] === undefined ||
|
||||
checkers[request.type](video) === false
|
||||
) {
|
||||
requests.splice(i, 1)
|
||||
}
|
||||
}
|
||||
function isVideoTorrentAddActivityValid (activity: any) {
|
||||
return isBaseActivityValid(activity, 'Add') &&
|
||||
isVideoTorrentObjectValid(activity.object)
|
||||
}
|
||||
|
||||
function removeBadRequestVideosQadu (requests: any[]) {
|
||||
for (let i = requests.length - 1; i >= 0 ; i--) {
|
||||
const request = requests[i]
|
||||
const video = request.data
|
||||
|
||||
if (
|
||||
!video ||
|
||||
(
|
||||
isUUIDValid(video.uuid) &&
|
||||
(has(video, 'views') === false || isVideoViewsValid(video.views)) &&
|
||||
(has(video, 'likes') === false || isVideoLikesValid(video.likes)) &&
|
||||
(has(video, 'dislikes') === false || isVideoDislikesValid(video.dislikes))
|
||||
) === false
|
||||
) {
|
||||
requests.splice(i, 1)
|
||||
}
|
||||
}
|
||||
function isVideoTorrentUpdateActivityValid (activity: any) {
|
||||
return isBaseActivityValid(activity, 'Update') &&
|
||||
isVideoTorrentObjectValid(activity.object)
|
||||
}
|
||||
|
||||
function removeBadRequestVideosEvents (requests: any[]) {
|
||||
for (let i = requests.length - 1; i >= 0 ; i--) {
|
||||
const request = requests[i]
|
||||
const eventData = request.data
|
||||
function isVideoTorrentObjectValid (video: any) {
|
||||
return video.type === 'Video' &&
|
||||
isVideoNameValid(video.name) &&
|
||||
isVideoDurationValid(video.duration) &&
|
||||
isUUIDValid(video.uuid) &&
|
||||
setValidRemoteTags(video) &&
|
||||
isRemoteIdentifierValid(video.category) &&
|
||||
isRemoteIdentifierValid(video.licence) &&
|
||||
isRemoteIdentifierValid(video.language) &&
|
||||
isVideoViewsValid(video.video) &&
|
||||
isVideoNSFWValid(video.nsfw) &&
|
||||
isDateValid(video.published) &&
|
||||
isDateValid(video.updated) &&
|
||||
isRemoteVideoContentValid(video.mediaType, video.content) &&
|
||||
isRemoteVideoIconValid(video.icon) &&
|
||||
setValidRemoteVideoUrls(video.url)
|
||||
}
|
||||
|
||||
if (
|
||||
!eventData ||
|
||||
(
|
||||
isUUIDValid(eventData.uuid) &&
|
||||
values(REQUEST_VIDEO_EVENT_TYPES).indexOf(eventData.eventType) !== -1 &&
|
||||
isVideoEventCountValid(eventData.count)
|
||||
) === false
|
||||
) {
|
||||
requests.splice(i, 1)
|
||||
}
|
||||
}
|
||||
function isVideoChannelCreateActivityValid (activity: any) {
|
||||
return isBaseActivityValid(activity, 'Create') &&
|
||||
isVideoChannelObjectValid(activity.object)
|
||||
}
|
||||
|
||||
function isVideoChannelUpdateActivityValid (activity: any) {
|
||||
return isBaseActivityValid(activity, 'Update') &&
|
||||
isVideoChannelObjectValid(activity.object)
|
||||
}
|
||||
|
||||
function isVideoChannelObjectValid (videoChannel: any) {
|
||||
return videoChannel.type === 'VideoChannel' &&
|
||||
isVideoChannelNameValid(videoChannel.name) &&
|
||||
isVideoChannelDescriptionValid(videoChannel.description) &&
|
||||
isUUIDValid(videoChannel.uuid)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export {
|
||||
removeBadRequestVideos,
|
||||
removeBadRequestVideosQadu,
|
||||
removeBadRequestVideosEvents
|
||||
isVideoTorrentAddActivityValid,
|
||||
isVideoChannelCreateActivityValid,
|
||||
isVideoTorrentUpdateActivityValid,
|
||||
isVideoChannelUpdateActivityValid
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
function isCommonVideoAttributesValid (video: any) {
|
||||
return isDateValid(video.createdAt) &&
|
||||
isDateValid(video.updatedAt) &&
|
||||
isRemoteVideoCategoryValid(video.category) &&
|
||||
isRemoteVideoLicenceValid(video.licence) &&
|
||||
isRemoteVideoLanguageValid(video.language) &&
|
||||
isVideoNSFWValid(video.nsfw) &&
|
||||
isVideoTruncatedDescriptionValid(video.truncatedDescription) &&
|
||||
isVideoDurationValid(video.duration) &&
|
||||
isVideoNameValid(video.name) &&
|
||||
isVideoTagsValid(video.tags) &&
|
||||
isUUIDValid(video.uuid) &&
|
||||
isVideoViewsValid(video.views) &&
|
||||
isVideoLikesValid(video.likes) &&
|
||||
isVideoDislikesValid(video.dislikes) &&
|
||||
isArray(video.files) &&
|
||||
video.files.every(videoFile => {
|
||||
if (!videoFile) return false
|
||||
function setValidRemoteTags (video: any) {
|
||||
if (Array.isArray(video.tag) === false) return false
|
||||
|
||||
return (
|
||||
isVideoFileInfoHashValid(videoFile.infoHash) &&
|
||||
isVideoFileExtnameValid(videoFile.extname) &&
|
||||
isVideoFileResolutionValid(videoFile.resolution)
|
||||
)
|
||||
})
|
||||
const newTag = video.tag.filter(t => {
|
||||
return t.type === 'Hashtag' &&
|
||||
isVideoTagValid(t.name)
|
||||
})
|
||||
|
||||
video.tag = newTag
|
||||
return true
|
||||
}
|
||||
|
||||
function checkAddVideo (video: any) {
|
||||
return isCommonVideoAttributesValid(video) &&
|
||||
isUUIDValid(video.channelUUID) &&
|
||||
isVideoThumbnailDataValid(video.thumbnailData)
|
||||
function isRemoteIdentifierValid (data: any) {
|
||||
return validator.isInt(data.identifier, { min: 0 })
|
||||
}
|
||||
|
||||
function checkUpdateVideo (video: any) {
|
||||
return isCommonVideoAttributesValid(video)
|
||||
function isRemoteVideoContentValid (mediaType: string, content: string) {
|
||||
return mediaType === 'text/markdown' && isVideoTruncatedDescriptionValid(content)
|
||||
}
|
||||
|
||||
function checkRemoveVideo (video: any) {
|
||||
return isUUIDValid(video.uuid)
|
||||
function isRemoteVideoIconValid (icon: any) {
|
||||
return icon.type === 'Image' &&
|
||||
validator.isURL(icon.url) &&
|
||||
icon.mediaType === 'image/jpeg' &&
|
||||
validator.isInt(icon.width, { min: 0 }) &&
|
||||
validator.isInt(icon.height, { min: 0 })
|
||||
}
|
||||
|
||||
function checkReportVideo (abuse: any) {
|
||||
return isUUIDValid(abuse.videoUUID) &&
|
||||
isVideoAbuseReasonValid(abuse.reportReason) &&
|
||||
isVideoAbuseReporterUsernameValid(abuse.reporterUsername)
|
||||
function setValidRemoteVideoUrls (video: any) {
|
||||
if (Array.isArray(video.url) === false) return false
|
||||
|
||||
const newUrl = video.url.filter(u => isRemoteVideoUrlValid(u))
|
||||
video.url = newUrl
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
function checkAddVideoChannel (videoChannel: any) {
|
||||
return isUUIDValid(videoChannel.uuid) &&
|
||||
isVideoChannelNameValid(videoChannel.name) &&
|
||||
isVideoChannelDescriptionValid(videoChannel.description) &&
|
||||
isDateValid(videoChannel.createdAt) &&
|
||||
isDateValid(videoChannel.updatedAt) &&
|
||||
isUUIDValid(videoChannel.ownerUUID)
|
||||
}
|
||||
|
||||
function checkUpdateVideoChannel (videoChannel: any) {
|
||||
return isUUIDValid(videoChannel.uuid) &&
|
||||
isVideoChannelNameValid(videoChannel.name) &&
|
||||
isVideoChannelDescriptionValid(videoChannel.description) &&
|
||||
isDateValid(videoChannel.createdAt) &&
|
||||
isDateValid(videoChannel.updatedAt) &&
|
||||
isUUIDValid(videoChannel.ownerUUID)
|
||||
}
|
||||
|
||||
function checkRemoveVideoChannel (videoChannel: any) {
|
||||
return isUUIDValid(videoChannel.uuid)
|
||||
}
|
||||
|
||||
function checkAddAuthor (author: any) {
|
||||
return isUUIDValid(author.uuid) &&
|
||||
isVideoAuthorNameValid(author.name)
|
||||
}
|
||||
|
||||
function checkRemoveAuthor (author: any) {
|
||||
return isUUIDValid(author.uuid)
|
||||
function isRemoteVideoUrlValid (url: any) {
|
||||
return url.type === 'Link' &&
|
||||
ACTIVITY_PUB.VIDEO_URL_MIME_TYPES.indexOf(url.mimeType) !== -1 &&
|
||||
validator.isURL(url.url) &&
|
||||
validator.isInt(url.width, { min: 0 }) &&
|
||||
validator.isInt(url.size, { min: 0 })
|
||||
}
|
||||
|
|
|
@ -3,6 +3,5 @@ export * from './misc'
|
|||
export * from './pods'
|
||||
export * from './pods'
|
||||
export * from './users'
|
||||
export * from './video-authors'
|
||||
export * from './video-channels'
|
||||
export * from './videos'
|
||||
|
|
|
@ -1,45 +0,0 @@
|
|||
import * as Promise from 'bluebird'
|
||||
import * as validator from 'validator'
|
||||
import * as express from 'express'
|
||||
import 'express-validator'
|
||||
|
||||
import { database as db } from '../../initializers'
|
||||
import { AuthorInstance } from '../../models'
|
||||
import { logger } from '../logger'
|
||||
|
||||
import { isUserUsernameValid } from './users'
|
||||
|
||||
function isVideoAuthorNameValid (value: string) {
|
||||
return isUserUsernameValid(value)
|
||||
}
|
||||
|
||||
function checkVideoAuthorExists (id: string, res: express.Response, callback: () => void) {
|
||||
let promise: Promise<AuthorInstance>
|
||||
if (validator.isInt(id)) {
|
||||
promise = db.Author.load(+id)
|
||||
} else { // UUID
|
||||
promise = db.Author.loadByUUID(id)
|
||||
}
|
||||
|
||||
promise.then(author => {
|
||||
if (!author) {
|
||||
return res.status(404)
|
||||
.json({ error: 'Video author not found' })
|
||||
.end()
|
||||
}
|
||||
|
||||
res.locals.author = author
|
||||
callback()
|
||||
})
|
||||
.catch(err => {
|
||||
logger.error('Error in video author request validator.', err)
|
||||
return res.sendStatus(500)
|
||||
})
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export {
|
||||
checkVideoAuthorExists,
|
||||
isVideoAuthorNameValid
|
||||
}
|
|
@ -73,19 +73,26 @@ function isVideoDescriptionValid (value: string) {
|
|||
}
|
||||
|
||||
function isVideoDurationValid (value: string) {
|
||||
return exists(value) && validator.isInt(value + '', VIDEOS_CONSTRAINTS_FIELDS.DURATION)
|
||||
// https://www.w3.org/TR/activitystreams-vocabulary/#dfn-duration
|
||||
return exists(value) &&
|
||||
typeof value === 'string' &&
|
||||
value.startsWith('PT') &&
|
||||
value.endsWith('S') &&
|
||||
validator.isInt(value.replace(/[^0-9]+/, ''), VIDEOS_CONSTRAINTS_FIELDS.DURATION)
|
||||
}
|
||||
|
||||
function isVideoNameValid (value: string) {
|
||||
return exists(value) && validator.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.NAME)
|
||||
}
|
||||
|
||||
function isVideoTagValid (tag: string) {
|
||||
return exists(tag) && validator.isLength(tag, VIDEOS_CONSTRAINTS_FIELDS.TAG)
|
||||
}
|
||||
|
||||
function isVideoTagsValid (tags: string[]) {
|
||||
return isArray(tags) &&
|
||||
validator.isInt(tags.length.toString(), VIDEOS_CONSTRAINTS_FIELDS.TAGS) &&
|
||||
tags.every(tag => {
|
||||
return exists(tag) && validator.isLength(tag, VIDEOS_CONSTRAINTS_FIELDS.TAG)
|
||||
})
|
||||
tags.every(tag => isVideoTagValid(tag))
|
||||
}
|
||||
|
||||
function isVideoThumbnailValid (value: string) {
|
||||
|
@ -209,6 +216,7 @@ export {
|
|||
isRemoteVideoPrivacyValid,
|
||||
isVideoFileResolutionValid,
|
||||
checkVideoExists,
|
||||
isVideoTagValid,
|
||||
isRemoteVideoCategoryValid,
|
||||
isRemoteVideoLicenceValid,
|
||||
isRemoteVideoLanguageValid
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue