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