X-Git-Url: https://git.immae.eu/?a=blobdiff_plain;f=server%2Fhelpers%2Fcustom-validators%2Factivitypub%2Factor.ts;h=2f44522a5813979def3f09694ec5c7ce165bb920;hb=f2eb23cd87cf32b8fe545178143b5f49e06a58da;hp=9a0bb32c16bd753d5c142e388fcba8ab8d19d3b3;hpb=938d3fa0ffb4cf3e4c88f94dcfcae5eb2ce5e190;p=github%2FChocobozzz%2FPeerTube.git diff --git a/server/helpers/custom-validators/activitypub/actor.ts b/server/helpers/custom-validators/activitypub/actor.ts index 9a0bb32c1..2f44522a5 100644 --- a/server/helpers/custom-validators/activitypub/actor.ts +++ b/server/helpers/custom-validators/activitypub/actor.ts @@ -1,11 +1,17 @@ -import * as validator from 'validator' -import { CONSTRAINTS_FIELDS } from '../../../initializers' -import { exists } from '../misc' -import { truncate } from 'lodash' +import validator from 'validator' +import { CONSTRAINTS_FIELDS } from '../../../initializers/constants' +import { exists, isArray } from '../misc' import { isActivityPubUrlValid, isBaseActivityValid, setValidAttributedTo } from './misc' +import { isHostValid } from '../servers' +import { peertubeTruncate } from '@server/helpers/core-utils' function isActorEndpointsObjectValid (endpointObject: any) { - return isActivityPubUrlValid(endpointObject.sharedInbox) + if (endpointObject?.sharedInbox) { + return isActivityPubUrlValid(endpointObject.sharedInbox) + } + + // Shared inbox is optional + return true } function isActorPublicKeyObjectValid (publicKeyObject: any) { @@ -15,18 +21,19 @@ function isActorPublicKeyObjectValid (publicKeyObject: any) { } function isActorTypeValid (type: string) { - return type === 'Person' || type === 'Application' || type === 'Group' + return type === 'Person' || type === 'Application' || type === 'Group' || type === 'Service' || type === 'Organization' } function isActorPublicKeyValid (publicKey: string) { return exists(publicKey) && typeof publicKey === 'string' && publicKey.startsWith('-----BEGIN PUBLIC KEY-----') && - publicKey.indexOf('-----END PUBLIC KEY-----') !== -1 && + publicKey.includes('-----END PUBLIC KEY-----') && validator.isLength(publicKey, CONSTRAINTS_FIELDS.ACTORS.PUBLIC_KEY) } -const actorNameRegExp = new RegExp('[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_]+') +const actorNameAlphabet = '[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\\-_.:]' +const actorNameRegExp = new RegExp(`^${actorNameAlphabet}+$`) function isActorPreferredUsernameValid (preferredUsername: string) { return exists(preferredUsername) && validator.matches(preferredUsername, actorNameRegExp) } @@ -36,7 +43,7 @@ function isActorPrivateKeyValid (privateKey: string) { typeof privateKey === 'string' && privateKey.startsWith('-----BEGIN RSA PRIVATE KEY-----') && // Sometimes there is a \n at the end, so just assert the string contains the end mark - privateKey.indexOf('-----END RSA PRIVATE KEY-----') !== -1 && + privateKey.includes('-----END RSA PRIVATE KEY-----') && validator.isLength(privateKey, CONSTRAINTS_FIELDS.ACTORS.PRIVATE_KEY) } @@ -44,19 +51,20 @@ function isActorObjectValid (actor: any) { return exists(actor) && isActivityPubUrlValid(actor.id) && isActorTypeValid(actor.type) && - isActivityPubUrlValid(actor.following) && - isActivityPubUrlValid(actor.followers) && isActivityPubUrlValid(actor.inbox) && - isActivityPubUrlValid(actor.outbox) && isActorPreferredUsernameValid(actor.preferredUsername) && isActivityPubUrlValid(actor.url) && isActorPublicKeyObjectValid(actor.publicKey) && isActorEndpointsObjectValid(actor.endpoints) && - setValidAttributedTo(actor) && - // If this is not an account, it should be attributed to an account + (!actor.outbox || isActivityPubUrlValid(actor.outbox)) && + (!actor.following || isActivityPubUrlValid(actor.following)) && + (!actor.followers || isActivityPubUrlValid(actor.followers)) && + + setValidAttributedTo(actor) && + // If this is a group (a channel), it should be attributed to an account // In PeerTube we use this to attach a video channel to a specific account - (actor.type === 'Person' || actor.attributedTo.length !== 0) + (actor.type !== 'Group' || actor.attributedTo.length !== 0) } function isActorFollowingCountValid (value: string) { @@ -71,48 +79,49 @@ function isActorDeleteActivityValid (activity: any) { return isBaseActivityValid(activity, 'Delete') } -function isActorFollowActivityValid (activity: any) { - return isBaseActivityValid(activity, 'Follow') && - isActivityPubUrlValid(activity.object) -} - -function isActorAcceptActivityValid (activity: any) { - return isBaseActivityValid(activity, 'Accept') -} - -function isActorRejectActivityValid (activity: any) { - return isBaseActivityValid(activity, 'Reject') -} - -function isActorUpdateActivityValid (activity: any) { - normalizeActor(activity.object) +function sanitizeAndCheckActorObject (object: any) { + normalizeActor(object) - return isBaseActivityValid(activity, 'Update') && - isActorObjectValid(activity.object) + return isActorObjectValid(object) } function normalizeActor (actor: any) { if (!actor) return - if (typeof actor.url !== 'string') { + if (!actor.url) { + actor.url = actor.id + } else if (typeof actor.url !== 'string') { actor.url = actor.url.href || actor.url.url } if (actor.summary && typeof actor.summary === 'string') { - actor.summary = truncate(actor.summary, { length: CONSTRAINTS_FIELDS.USERS.DESCRIPTION.max }) + actor.summary = peertubeTruncate(actor.summary, { length: CONSTRAINTS_FIELDS.USERS.DESCRIPTION.max }) if (actor.summary.length < CONSTRAINTS_FIELDS.USERS.DESCRIPTION.min) { actor.summary = null } } +} + +function isValidActorHandle (handle: string) { + if (!exists(handle)) return false + + const parts = handle.split('@') + if (parts.length !== 2) return false + + return isHostValid(parts[1]) +} - return +function areValidActorHandles (handles: string[]) { + return isArray(handles) && handles.every(h => isValidActorHandle(h)) } // --------------------------------------------------------------------------- export { normalizeActor, + actorNameAlphabet, + areValidActorHandles, isActorEndpointsObjectValid, isActorPublicKeyObjectValid, isActorTypeValid, @@ -122,9 +131,7 @@ export { isActorObjectValid, isActorFollowingCountValid, isActorFollowersCountValid, - isActorFollowActivityValid, - isActorAcceptActivityValid, - isActorRejectActivityValid, isActorDeleteActivityValid, - isActorUpdateActivityValid + sanitizeAndCheckActorObject, + isValidActorHandle }