From e4f97babf701481b55cc10fb3448feab5f97c867 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Thu, 9 Nov 2017 17:51:58 +0100 Subject: Begin activitypub --- .../custom-validators/activitypub/account.ts | 123 ++++++++++++++ .../helpers/custom-validators/activitypub/index.ts | 4 + .../helpers/custom-validators/activitypub/misc.ts | 17 ++ .../custom-validators/activitypub/signature.ts | 22 +++ .../custom-validators/activitypub/videos.ts | 184 +++++++++++++++++++++ server/helpers/custom-validators/index.ts | 2 +- server/helpers/custom-validators/remote/index.ts | 1 - server/helpers/custom-validators/remote/videos.ts | 184 --------------------- 8 files changed, 351 insertions(+), 186 deletions(-) create mode 100644 server/helpers/custom-validators/activitypub/account.ts create mode 100644 server/helpers/custom-validators/activitypub/index.ts create mode 100644 server/helpers/custom-validators/activitypub/misc.ts create mode 100644 server/helpers/custom-validators/activitypub/signature.ts create mode 100644 server/helpers/custom-validators/activitypub/videos.ts delete mode 100644 server/helpers/custom-validators/remote/index.ts delete mode 100644 server/helpers/custom-validators/remote/videos.ts (limited to 'server/helpers/custom-validators') diff --git a/server/helpers/custom-validators/activitypub/account.ts b/server/helpers/custom-validators/activitypub/account.ts new file mode 100644 index 000000000..8a7d1b7fe --- /dev/null +++ b/server/helpers/custom-validators/activitypub/account.ts @@ -0,0 +1,123 @@ +import * as validator from 'validator' + +import { exists, isUUIDValid } from '../misc' +import { isActivityPubUrlValid } from './misc' +import { isUserUsernameValid } from '../users' + +function isAccountEndpointsObjectValid (endpointObject: any) { + return isAccountSharedInboxValid(endpointObject.sharedInbox) +} + +function isAccountSharedInboxValid (sharedInbox: string) { + return isActivityPubUrlValid(sharedInbox) +} + +function isAccountPublicKeyObjectValid (publicKeyObject: any) { + return isAccountPublicKeyIdValid(publicKeyObject.id) && + isAccountPublicKeyOwnerValid(publicKeyObject.owner) && + isAccountPublicKeyValid(publicKeyObject.publicKeyPem) +} + +function isAccountPublicKeyIdValid (id: string) { + return isActivityPubUrlValid(id) +} + +function isAccountTypeValid (type: string) { + return type === 'Person' || type === 'Application' +} + +function isAccountPublicKeyOwnerValid (owner: string) { + return isActivityPubUrlValid(owner) +} + +function isAccountPublicKeyValid (publicKey: string) { + return exists(publicKey) && + typeof publicKey === 'string' && + publicKey.startsWith('-----BEGIN PUBLIC KEY-----') && + publicKey.endsWith('-----END PUBLIC KEY-----') +} + +function isAccountIdValid (id: string) { + return isActivityPubUrlValid(id) +} + +function isAccountFollowingValid (id: string) { + return isActivityPubUrlValid(id) +} + +function isAccountFollowersValid (id: string) { + return isActivityPubUrlValid(id) +} + +function isAccountInboxValid (inbox: string) { + return isActivityPubUrlValid(inbox) +} + +function isAccountOutboxValid (outbox: string) { + return isActivityPubUrlValid(outbox) +} + +function isAccountNameValid (name: string) { + return isUserUsernameValid(name) +} + +function isAccountPreferredUsernameValid (preferredUsername: string) { + return isAccountNameValid(preferredUsername) +} + +function isAccountUrlValid (url: string) { + return isActivityPubUrlValid(url) +} + +function isAccountPrivateKeyValid (privateKey: string) { + return exists(privateKey) && + typeof privateKey === 'string' && + privateKey.startsWith('-----BEGIN RSA PRIVATE KEY-----') && + privateKey.endsWith('-----END RSA PRIVATE KEY-----') +} + +function isRemoteAccountValid (remoteAccount: any) { + return isAccountIdValid(remoteAccount.id) && + isUUIDValid(remoteAccount.uuid) && + isAccountTypeValid(remoteAccount.type) && + isAccountFollowingValid(remoteAccount.following) && + isAccountFollowersValid(remoteAccount.followers) && + isAccountInboxValid(remoteAccount.inbox) && + isAccountOutboxValid(remoteAccount.outbox) && + isAccountPreferredUsernameValid(remoteAccount.preferredUsername) && + isAccountUrlValid(remoteAccount.url) && + isAccountPublicKeyObjectValid(remoteAccount.publicKey) && + isAccountEndpointsObjectValid(remoteAccount.endpoint) +} + +function isAccountFollowingCountValid (value: string) { + return exists(value) && validator.isInt('' + value, { min: 0 }) +} + +function isAccountFollowersCountValid (value: string) { + return exists(value) && validator.isInt('' + value, { min: 0 }) +} + +// --------------------------------------------------------------------------- + +export { + isAccountEndpointsObjectValid, + isAccountSharedInboxValid, + isAccountPublicKeyObjectValid, + isAccountPublicKeyIdValid, + isAccountTypeValid, + isAccountPublicKeyOwnerValid, + isAccountPublicKeyValid, + isAccountIdValid, + isAccountFollowingValid, + isAccountFollowersValid, + isAccountInboxValid, + isAccountOutboxValid, + isAccountPreferredUsernameValid, + isAccountUrlValid, + isAccountPrivateKeyValid, + isRemoteAccountValid, + isAccountFollowingCountValid, + isAccountFollowersCountValid, + isAccountNameValid +} diff --git a/server/helpers/custom-validators/activitypub/index.ts b/server/helpers/custom-validators/activitypub/index.ts new file mode 100644 index 000000000..800f0ddf3 --- /dev/null +++ b/server/helpers/custom-validators/activitypub/index.ts @@ -0,0 +1,4 @@ +export * from './account' +export * from './signature' +export * from './misc' +export * from './videos' diff --git a/server/helpers/custom-validators/activitypub/misc.ts b/server/helpers/custom-validators/activitypub/misc.ts new file mode 100644 index 000000000..806d33483 --- /dev/null +++ b/server/helpers/custom-validators/activitypub/misc.ts @@ -0,0 +1,17 @@ +import { exists } from '../misc' + +function isActivityPubUrlValid (url: string) { + const isURLOptions = { + require_host: true, + require_tld: true, + require_protocol: true, + require_valid_protocol: true, + protocols: [ 'http', 'https' ] + } + + return exists(url) && validator.isURL(url, isURLOptions) +} + +export { + isActivityPubUrlValid +} diff --git a/server/helpers/custom-validators/activitypub/signature.ts b/server/helpers/custom-validators/activitypub/signature.ts new file mode 100644 index 000000000..683ed2b1c --- /dev/null +++ b/server/helpers/custom-validators/activitypub/signature.ts @@ -0,0 +1,22 @@ +import { exists } from '../misc' +import { isActivityPubUrlValid } from './misc' + +function isSignatureTypeValid (signatureType: string) { + return exists(signatureType) && signatureType === 'GraphSignature2012' +} + +function isSignatureCreatorValid (signatureCreator: string) { + return exists(signatureCreator) && isActivityPubUrlValid(signatureCreator) +} + +function isSignatureValueValid (signatureValue: string) { + return exists(signatureValue) && signatureValue.length > 0 +} + +// --------------------------------------------------------------------------- + +export { + isSignatureTypeValid, + isSignatureCreatorValid, + isSignatureValueValid +} diff --git a/server/helpers/custom-validators/activitypub/videos.ts b/server/helpers/custom-validators/activitypub/videos.ts new file mode 100644 index 000000000..e0ffba679 --- /dev/null +++ b/server/helpers/custom-validators/activitypub/videos.ts @@ -0,0 +1,184 @@ +import 'express-validator' +import { has, values } from 'lodash' + +import { + REQUEST_ENDPOINTS, + REQUEST_ENDPOINT_ACTIONS, + REQUEST_VIDEO_EVENT_TYPES +} from '../../../initializers' +import { isArray, isDateValid, isUUIDValid } from '../misc' +import { + isVideoThumbnailDataValid, + isVideoAbuseReasonValid, + isVideoAbuseReporterUsernameValid, + isVideoViewsValid, + isVideoLikesValid, + isVideoDislikesValid, + isVideoEventCountValid, + isRemoteVideoCategoryValid, + isRemoteVideoLicenceValid, + isRemoteVideoLanguageValid, + isVideoNSFWValid, + isVideoTruncatedDescriptionValid, + isVideoDurationValid, + isVideoFileInfoHashValid, + isVideoNameValid, + isVideoTagsValid, + isVideoFileExtnameValid, + isVideoFileResolutionValid +} from '../videos' +import { isVideoChannelDescriptionValid, isVideoChannelNameValid } from '../video-channels' +import { isVideoAuthorNameValid } from '../video-authors' + +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 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 removeBadRequestVideosEvents (requests: any[]) { + for (let i = requests.length - 1; i >= 0 ; i--) { + const request = requests[i] + const eventData = request.data + + if ( + !eventData || + ( + isUUIDValid(eventData.uuid) && + values(REQUEST_VIDEO_EVENT_TYPES).indexOf(eventData.eventType) !== -1 && + isVideoEventCountValid(eventData.count) + ) === false + ) { + requests.splice(i, 1) + } + } +} + +// --------------------------------------------------------------------------- + +export { + removeBadRequestVideos, + removeBadRequestVideosQadu, + removeBadRequestVideosEvents +} + +// --------------------------------------------------------------------------- + +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 + + return ( + isVideoFileInfoHashValid(videoFile.infoHash) && + isVideoFileExtnameValid(videoFile.extname) && + isVideoFileResolutionValid(videoFile.resolution) + ) + }) +} + +function checkAddVideo (video: any) { + return isCommonVideoAttributesValid(video) && + isUUIDValid(video.channelUUID) && + isVideoThumbnailDataValid(video.thumbnailData) +} + +function checkUpdateVideo (video: any) { + return isCommonVideoAttributesValid(video) +} + +function checkRemoveVideo (video: any) { + return isUUIDValid(video.uuid) +} + +function checkReportVideo (abuse: any) { + return isUUIDValid(abuse.videoUUID) && + isVideoAbuseReasonValid(abuse.reportReason) && + isVideoAbuseReporterUsernameValid(abuse.reporterUsername) +} + +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) +} diff --git a/server/helpers/custom-validators/index.ts b/server/helpers/custom-validators/index.ts index c79982660..869b08870 100644 --- a/server/helpers/custom-validators/index.ts +++ b/server/helpers/custom-validators/index.ts @@ -1,4 +1,4 @@ -export * from './remote' +export * from './activitypub' export * from './misc' export * from './pods' export * from './pods' diff --git a/server/helpers/custom-validators/remote/index.ts b/server/helpers/custom-validators/remote/index.ts deleted file mode 100644 index e29a9b767..000000000 --- a/server/helpers/custom-validators/remote/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './videos' diff --git a/server/helpers/custom-validators/remote/videos.ts b/server/helpers/custom-validators/remote/videos.ts deleted file mode 100644 index e0ffba679..000000000 --- a/server/helpers/custom-validators/remote/videos.ts +++ /dev/null @@ -1,184 +0,0 @@ -import 'express-validator' -import { has, values } from 'lodash' - -import { - REQUEST_ENDPOINTS, - REQUEST_ENDPOINT_ACTIONS, - REQUEST_VIDEO_EVENT_TYPES -} from '../../../initializers' -import { isArray, isDateValid, isUUIDValid } from '../misc' -import { - isVideoThumbnailDataValid, - isVideoAbuseReasonValid, - isVideoAbuseReporterUsernameValid, - isVideoViewsValid, - isVideoLikesValid, - isVideoDislikesValid, - isVideoEventCountValid, - isRemoteVideoCategoryValid, - isRemoteVideoLicenceValid, - isRemoteVideoLanguageValid, - isVideoNSFWValid, - isVideoTruncatedDescriptionValid, - isVideoDurationValid, - isVideoFileInfoHashValid, - isVideoNameValid, - isVideoTagsValid, - isVideoFileExtnameValid, - isVideoFileResolutionValid -} from '../videos' -import { isVideoChannelDescriptionValid, isVideoChannelNameValid } from '../video-channels' -import { isVideoAuthorNameValid } from '../video-authors' - -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 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 removeBadRequestVideosEvents (requests: any[]) { - for (let i = requests.length - 1; i >= 0 ; i--) { - const request = requests[i] - const eventData = request.data - - if ( - !eventData || - ( - isUUIDValid(eventData.uuid) && - values(REQUEST_VIDEO_EVENT_TYPES).indexOf(eventData.eventType) !== -1 && - isVideoEventCountValid(eventData.count) - ) === false - ) { - requests.splice(i, 1) - } - } -} - -// --------------------------------------------------------------------------- - -export { - removeBadRequestVideos, - removeBadRequestVideosQadu, - removeBadRequestVideosEvents -} - -// --------------------------------------------------------------------------- - -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 - - return ( - isVideoFileInfoHashValid(videoFile.infoHash) && - isVideoFileExtnameValid(videoFile.extname) && - isVideoFileResolutionValid(videoFile.resolution) - ) - }) -} - -function checkAddVideo (video: any) { - return isCommonVideoAttributesValid(video) && - isUUIDValid(video.channelUUID) && - isVideoThumbnailDataValid(video.thumbnailData) -} - -function checkUpdateVideo (video: any) { - return isCommonVideoAttributesValid(video) -} - -function checkRemoveVideo (video: any) { - return isUUIDValid(video.uuid) -} - -function checkReportVideo (abuse: any) { - return isUUIDValid(abuse.videoUUID) && - isVideoAbuseReasonValid(abuse.reportReason) && - isVideoAbuseReporterUsernameValid(abuse.reporterUsername) -} - -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) -} -- cgit v1.2.3