]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blob - server/helpers/custom-validators/activitypub/actor.ts
Fix tests
[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 } 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 actorNameRegExp = new RegExp('^[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\\-_]+$')
31 function isActorPreferredUsernameValid (preferredUsername: string) {
32 return exists(preferredUsername) && validator.matches(preferredUsername, actorNameRegExp)
33 }
34
35 function isActorPrivateKeyValid (privateKey: string) {
36 return exists(privateKey) &&
37 typeof privateKey === 'string' &&
38 privateKey.startsWith('-----BEGIN RSA PRIVATE KEY-----') &&
39 // Sometimes there is a \n at the end, so just assert the string contains the end mark
40 privateKey.indexOf('-----END RSA PRIVATE KEY-----') !== -1 &&
41 validator.isLength(privateKey, CONSTRAINTS_FIELDS.ACTORS.PRIVATE_KEY)
42 }
43
44 function isActorObjectValid (actor: any) {
45 return exists(actor) &&
46 isActivityPubUrlValid(actor.id) &&
47 isActorTypeValid(actor.type) &&
48 isActivityPubUrlValid(actor.following) &&
49 isActivityPubUrlValid(actor.followers) &&
50 isActivityPubUrlValid(actor.inbox) &&
51 isActivityPubUrlValid(actor.outbox) &&
52 isActorPreferredUsernameValid(actor.preferredUsername) &&
53 isActivityPubUrlValid(actor.url) &&
54 isActorPublicKeyObjectValid(actor.publicKey) &&
55 isActorEndpointsObjectValid(actor.endpoints) &&
56 setValidAttributedTo(actor) &&
57
58 // If this is not an account, it should be attributed to an account
59 // In PeerTube we use this to attach a video channel to a specific account
60 (actor.type === 'Person' || actor.attributedTo.length !== 0)
61 }
62
63 function isActorFollowingCountValid (value: string) {
64 return exists(value) && validator.isInt('' + value, { min: 0 })
65 }
66
67 function isActorFollowersCountValid (value: string) {
68 return exists(value) && validator.isInt('' + value, { min: 0 })
69 }
70
71 function isActorDeleteActivityValid (activity: any) {
72 return isBaseActivityValid(activity, 'Delete')
73 }
74
75 function isActorFollowActivityValid (activity: any) {
76 return isBaseActivityValid(activity, 'Follow') &&
77 isActivityPubUrlValid(activity.object)
78 }
79
80 function isActorAcceptActivityValid (activity: any) {
81 return isBaseActivityValid(activity, 'Accept')
82 }
83
84 function isActorRejectActivityValid (activity: any) {
85 return isBaseActivityValid(activity, 'Reject')
86 }
87
88 function isActorUpdateActivityValid (activity: any) {
89 normalizeActor(activity.object)
90
91 return isBaseActivityValid(activity, 'Update') &&
92 isActorObjectValid(activity.object)
93 }
94
95 function normalizeActor (actor: any) {
96 if (!actor || !actor.url) return
97
98 if (typeof actor.url !== 'string') {
99 actor.url = actor.url.href || actor.url.url
100 }
101
102 if (actor.summary && typeof actor.summary === 'string') {
103 actor.summary = truncate(actor.summary, { length: CONSTRAINTS_FIELDS.USERS.DESCRIPTION.max })
104
105 if (actor.summary.length < CONSTRAINTS_FIELDS.USERS.DESCRIPTION.min) {
106 actor.summary = null
107 }
108 }
109
110 return
111 }
112
113 function isValidActorHandle (handle: string) {
114 if (!exists(handle)) return false
115
116 const parts = handle.split('@')
117 if (parts.length !== 2) return false
118
119 return isHostValid(parts[1])
120 }
121
122 // ---------------------------------------------------------------------------
123
124 export {
125 normalizeActor,
126 isActorEndpointsObjectValid,
127 isActorPublicKeyObjectValid,
128 isActorTypeValid,
129 isActorPublicKeyValid,
130 isActorPreferredUsernameValid,
131 isActorPrivateKeyValid,
132 isActorObjectValid,
133 isActorFollowingCountValid,
134 isActorFollowersCountValid,
135 isActorFollowActivityValid,
136 isActorAcceptActivityValid,
137 isActorRejectActivityValid,
138 isActorDeleteActivityValid,
139 isActorUpdateActivityValid,
140 isValidActorHandle
141 }