]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blob - server/helpers/custom-validators/activitypub/actor.ts
Enable video watch
[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 isActorFollowActivityValid (activity: any) {
77 return isBaseActivityValid(activity, 'Follow') &&
78 isActivityPubUrlValid(activity.object)
79 }
80
81 function isActorAcceptActivityValid (activity: any) {
82 return isBaseActivityValid(activity, 'Accept')
83 }
84
85 function isActorRejectActivityValid (activity: any) {
86 return isBaseActivityValid(activity, 'Reject')
87 }
88
89 function isActorUpdateActivityValid (activity: any) {
90 normalizeActor(activity.object)
91
92 return isBaseActivityValid(activity, 'Update') &&
93 isActorObjectValid(activity.object)
94 }
95
96 function normalizeActor (actor: any) {
97 if (!actor || !actor.url) return
98
99 if (typeof actor.url !== 'string') {
100 actor.url = actor.url.href || actor.url.url
101 }
102
103 if (actor.summary && typeof actor.summary === 'string') {
104 actor.summary = truncate(actor.summary, { length: CONSTRAINTS_FIELDS.USERS.DESCRIPTION.max })
105
106 if (actor.summary.length < CONSTRAINTS_FIELDS.USERS.DESCRIPTION.min) {
107 actor.summary = null
108 }
109 }
110
111 return
112 }
113
114 function isValidActorHandle (handle: string) {
115 if (!exists(handle)) return false
116
117 const parts = handle.split('@')
118 if (parts.length !== 2) return false
119
120 return isHostValid(parts[1])
121 }
122
123 function areValidActorHandles (handles: string[]) {
124 return isArray(handles) && handles.every(h => isValidActorHandle(h))
125 }
126
127 // ---------------------------------------------------------------------------
128
129 export {
130 normalizeActor,
131 actorNameAlphabet,
132 areValidActorHandles,
133 isActorEndpointsObjectValid,
134 isActorPublicKeyObjectValid,
135 isActorTypeValid,
136 isActorPublicKeyValid,
137 isActorPreferredUsernameValid,
138 isActorPrivateKeyValid,
139 isActorObjectValid,
140 isActorFollowingCountValid,
141 isActorFollowersCountValid,
142 isActorFollowActivityValid,
143 isActorAcceptActivityValid,
144 isActorRejectActivityValid,
145 isActorDeleteActivityValid,
146 isActorUpdateActivityValid,
147 isValidActorHandle
148 }