]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame_incremental - server/helpers/custom-validators/activitypub/actor.ts
Basic video redundancy implementation
[github/Chocobozzz/PeerTube.git] / server / helpers / custom-validators / activitypub / actor.ts
... / ...
CommitLineData
1import * as validator from 'validator'
2import { CONSTRAINTS_FIELDS } from '../../../initializers'
3import { exists, isArray } from '../misc'
4import { truncate } from 'lodash'
5import { isActivityPubUrlValid, isBaseActivityValid, setValidAttributedTo } from './misc'
6import { isHostValid } from '../servers'
7
8function isActorEndpointsObjectValid (endpointObject: any) {
9 return isActivityPubUrlValid(endpointObject.sharedInbox)
10}
11
12function isActorPublicKeyObjectValid (publicKeyObject: any) {
13 return isActivityPubUrlValid(publicKeyObject.id) &&
14 isActivityPubUrlValid(publicKeyObject.owner) &&
15 isActorPublicKeyValid(publicKeyObject.publicKeyPem)
16}
17
18function isActorTypeValid (type: string) {
19 return type === 'Person' || type === 'Application' || type === 'Group'
20}
21
22function 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
30const actorNameRegExp = new RegExp('^[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\\-_]+$')
31function isActorPreferredUsernameValid (preferredUsername: string) {
32 return exists(preferredUsername) && validator.matches(preferredUsername, actorNameRegExp)
33}
34
35function 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
44function 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
63function isActorFollowingCountValid (value: string) {
64 return exists(value) && validator.isInt('' + value, { min: 0 })
65}
66
67function isActorFollowersCountValid (value: string) {
68 return exists(value) && validator.isInt('' + value, { min: 0 })
69}
70
71function isActorDeleteActivityValid (activity: any) {
72 return isBaseActivityValid(activity, 'Delete')
73}
74
75function isActorFollowActivityValid (activity: any) {
76 return isBaseActivityValid(activity, 'Follow') &&
77 isActivityPubUrlValid(activity.object)
78}
79
80function isActorAcceptActivityValid (activity: any) {
81 return isBaseActivityValid(activity, 'Accept')
82}
83
84function isActorRejectActivityValid (activity: any) {
85 return isBaseActivityValid(activity, 'Reject')
86}
87
88function isActorUpdateActivityValid (activity: any) {
89 normalizeActor(activity.object)
90
91 return isBaseActivityValid(activity, 'Update') &&
92 isActorObjectValid(activity.object)
93}
94
95function 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
113function 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
122function areValidActorHandles (handles: string[]) {
123 return isArray(handles) && handles.every(h => isValidActorHandle(h))
124}
125
126// ---------------------------------------------------------------------------
127
128export {
129 normalizeActor,
130 areValidActorHandles,
131 isActorEndpointsObjectValid,
132 isActorPublicKeyObjectValid,
133 isActorTypeValid,
134 isActorPublicKeyValid,
135 isActorPreferredUsernameValid,
136 isActorPrivateKeyValid,
137 isActorObjectValid,
138 isActorFollowingCountValid,
139 isActorFollowersCountValid,
140 isActorFollowActivityValid,
141 isActorAcceptActivityValid,
142 isActorRejectActivityValid,
143 isActorDeleteActivityValid,
144 isActorUpdateActivityValid,
145 isValidActorHandle
146}