]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame_incremental - server/helpers/custom-validators/activitypub/actor.ts
Fix theme npm link
[github/Chocobozzz/PeerTube.git] / server / helpers / custom-validators / activitypub / actor.ts
... / ...
CommitLineData
1import validator from 'validator'
2import { CONSTRAINTS_FIELDS } from '../../../initializers/constants'
3import { exists, isArray } from '../misc'
4import { isActivityPubUrlValid, isBaseActivityValid, setValidAttributedTo } from './misc'
5import { isHostValid } from '../servers'
6import { peertubeTruncate } from '@server/helpers/core-utils'
7
8function isActorEndpointsObjectValid (endpointObject: any) {
9 if (endpointObject?.sharedInbox) {
10 return isActivityPubUrlValid(endpointObject.sharedInbox)
11 }
12
13 // Shared inbox is optional
14 return true
15}
16
17function isActorPublicKeyObjectValid (publicKeyObject: any) {
18 return isActivityPubUrlValid(publicKeyObject.id) &&
19 isActivityPubUrlValid(publicKeyObject.owner) &&
20 isActorPublicKeyValid(publicKeyObject.publicKeyPem)
21}
22
23function isActorTypeValid (type: string) {
24 return type === 'Person' || type === 'Application' || type === 'Group' || type === 'Service' || type === 'Organization'
25}
26
27function isActorPublicKeyValid (publicKey: string) {
28 return exists(publicKey) &&
29 typeof publicKey === 'string' &&
30 publicKey.startsWith('-----BEGIN PUBLIC KEY-----') &&
31 publicKey.includes('-----END PUBLIC KEY-----') &&
32 validator.isLength(publicKey, CONSTRAINTS_FIELDS.ACTORS.PUBLIC_KEY)
33}
34
35const actorNameAlphabet = '[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\\-_.:]'
36const actorNameRegExp = new RegExp(`^${actorNameAlphabet}+$`)
37function isActorPreferredUsernameValid (preferredUsername: string) {
38 return exists(preferredUsername) && validator.matches(preferredUsername, actorNameRegExp)
39}
40
41function isActorPrivateKeyValid (privateKey: string) {
42 return exists(privateKey) &&
43 typeof privateKey === 'string' &&
44 privateKey.startsWith('-----BEGIN RSA PRIVATE KEY-----') &&
45 // Sometimes there is a \n at the end, so just assert the string contains the end mark
46 privateKey.includes('-----END RSA PRIVATE KEY-----') &&
47 validator.isLength(privateKey, CONSTRAINTS_FIELDS.ACTORS.PRIVATE_KEY)
48}
49
50function isActorObjectValid (actor: any) {
51 return exists(actor) &&
52 isActivityPubUrlValid(actor.id) &&
53 isActorTypeValid(actor.type) &&
54 isActivityPubUrlValid(actor.inbox) &&
55 isActorPreferredUsernameValid(actor.preferredUsername) &&
56 isActivityPubUrlValid(actor.url) &&
57 isActorPublicKeyObjectValid(actor.publicKey) &&
58 isActorEndpointsObjectValid(actor.endpoints) &&
59
60 (!actor.outbox || isActivityPubUrlValid(actor.outbox)) &&
61 (!actor.following || isActivityPubUrlValid(actor.following)) &&
62 (!actor.followers || isActivityPubUrlValid(actor.followers)) &&
63
64 setValidAttributedTo(actor) &&
65 // If this is a group (a channel), it should be attributed to an account
66 // In PeerTube we use this to attach a video channel to a specific account
67 (actor.type !== 'Group' || actor.attributedTo.length !== 0)
68}
69
70function isActorFollowingCountValid (value: string) {
71 return exists(value) && validator.isInt('' + value, { min: 0 })
72}
73
74function isActorFollowersCountValid (value: string) {
75 return exists(value) && validator.isInt('' + value, { min: 0 })
76}
77
78function isActorDeleteActivityValid (activity: any) {
79 return isBaseActivityValid(activity, 'Delete')
80}
81
82function sanitizeAndCheckActorObject (object: any) {
83 normalizeActor(object)
84
85 return isActorObjectValid(object)
86}
87
88function normalizeActor (actor: any) {
89 if (!actor) return
90
91 if (!actor.url) {
92 actor.url = actor.id
93 } else if (typeof actor.url !== 'string') {
94 actor.url = actor.url.href || actor.url.url
95 }
96
97 if (actor.summary && typeof actor.summary === 'string') {
98 actor.summary = peertubeTruncate(actor.summary, { length: CONSTRAINTS_FIELDS.USERS.DESCRIPTION.max })
99
100 if (actor.summary.length < CONSTRAINTS_FIELDS.USERS.DESCRIPTION.min) {
101 actor.summary = null
102 }
103 }
104}
105
106function isValidActorHandle (handle: string) {
107 if (!exists(handle)) return false
108
109 const parts = handle.split('@')
110 if (parts.length !== 2) return false
111
112 return isHostValid(parts[1])
113}
114
115function areValidActorHandles (handles: string[]) {
116 return isArray(handles) && handles.every(h => isValidActorHandle(h))
117}
118
119// ---------------------------------------------------------------------------
120
121export {
122 normalizeActor,
123 actorNameAlphabet,
124 areValidActorHandles,
125 isActorEndpointsObjectValid,
126 isActorPublicKeyObjectValid,
127 isActorTypeValid,
128 isActorPublicKeyValid,
129 isActorPreferredUsernameValid,
130 isActorPrivateKeyValid,
131 isActorObjectValid,
132 isActorFollowingCountValid,
133 isActorFollowersCountValid,
134 isActorDeleteActivityValid,
135 sanitizeAndCheckActorObject,
136 isValidActorHandle
137}