aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/helpers
diff options
context:
space:
mode:
Diffstat (limited to 'server/helpers')
-rw-r--r--server/helpers/activitypub.ts29
-rw-r--r--server/helpers/custom-validators/accounts.ts (renamed from server/helpers/custom-validators/video-accounts.ts)14
-rw-r--r--server/helpers/custom-validators/activitypub/account.ts31
-rw-r--r--server/helpers/custom-validators/activitypub/activity.ts19
-rw-r--r--server/helpers/custom-validators/activitypub/misc.ts10
-rw-r--r--server/helpers/custom-validators/activitypub/videos.ts14
-rw-r--r--server/helpers/custom-validators/index.ts3
-rw-r--r--server/helpers/custom-validators/webfinger.ts25
-rw-r--r--server/helpers/webfinger.ts21
9 files changed, 118 insertions, 48 deletions
diff --git a/server/helpers/activitypub.ts b/server/helpers/activitypub.ts
index a1493e5c1..b91490a0b 100644
--- a/server/helpers/activitypub.ts
+++ b/server/helpers/activitypub.ts
@@ -5,7 +5,7 @@ import { ActivityIconObject } from '../../shared/index'
5import { ActivityPubActor } from '../../shared/models/activitypub/activitypub-actor' 5import { ActivityPubActor } from '../../shared/models/activitypub/activitypub-actor'
6import { ResultList } from '../../shared/models/result-list.model' 6import { ResultList } from '../../shared/models/result-list.model'
7import { database as db, REMOTE_SCHEME } from '../initializers' 7import { database as db, REMOTE_SCHEME } from '../initializers'
8import { CONFIG, STATIC_PATHS } from '../initializers/constants' 8import { ACTIVITY_PUB_ACCEPT_HEADER, CONFIG, STATIC_PATHS } from '../initializers/constants'
9import { VideoInstance } from '../models/video/video-interface' 9import { VideoInstance } from '../models/video/video-interface'
10import { isRemoteAccountValid } from './custom-validators' 10import { isRemoteAccountValid } from './custom-validators'
11import { logger } from './logger' 11import { logger } from './logger'
@@ -35,11 +35,11 @@ async function getOrCreateAccount (accountUrl: string) {
35 35
36 // We don't have this account in our database, fetch it on remote 36 // We don't have this account in our database, fetch it on remote
37 if (!account) { 37 if (!account) {
38 const { account } = await fetchRemoteAccountAndCreatePod(accountUrl) 38 const res = await fetchRemoteAccountAndCreatePod(accountUrl)
39 39 if (res === undefined) throw new Error('Cannot fetch remote account.')
40 if (!account) throw new Error('Cannot fetch remote account.')
41 40
42 // Save our new account in database 41 // Save our new account in database
42 const account = res.account
43 await account.save() 43 await account.save()
44 } 44 }
45 45
@@ -49,19 +49,27 @@ async function getOrCreateAccount (accountUrl: string) {
49async function fetchRemoteAccountAndCreatePod (accountUrl: string) { 49async function fetchRemoteAccountAndCreatePod (accountUrl: string) {
50 const options = { 50 const options = {
51 uri: accountUrl, 51 uri: accountUrl,
52 method: 'GET' 52 method: 'GET',
53 headers: {
54 'Accept': ACTIVITY_PUB_ACCEPT_HEADER
55 }
53 } 56 }
54 57
58 logger.info('Fetching remote account %s.', accountUrl)
59
55 let requestResult 60 let requestResult
56 try { 61 try {
57 requestResult = await doRequest(options) 62 requestResult = await doRequest(options)
58 } catch (err) { 63 } catch (err) {
59 logger.warning('Cannot fetch remote account %s.', accountUrl, err) 64 logger.warn('Cannot fetch remote account %s.', accountUrl, err)
60 return undefined 65 return undefined
61 } 66 }
62 67
63 const accountJSON: ActivityPubActor = requestResult.body 68 const accountJSON: ActivityPubActor = JSON.parse(requestResult.body)
64 if (isRemoteAccountValid(accountJSON) === false) return undefined 69 if (isRemoteAccountValid(accountJSON) === false) {
70 logger.debug('Remote account JSON is not valid.', { accountJSON })
71 return undefined
72 }
65 73
66 const followersCount = await fetchAccountCount(accountJSON.followers) 74 const followersCount = await fetchAccountCount(accountJSON.followers)
67 const followingCount = await fetchAccountCount(accountJSON.following) 75 const followingCount = await fetchAccountCount(accountJSON.following)
@@ -90,7 +98,8 @@ async function fetchRemoteAccountAndCreatePod (accountUrl: string) {
90 host: accountHost 98 host: accountHost
91 } 99 }
92 } 100 }
93 const pod = await db.Pod.findOrCreate(podOptions) 101 const [ pod ] = await db.Pod.findOrCreate(podOptions)
102 account.set('podId', pod.id)
94 103
95 return { account, pod } 104 return { account, pod }
96} 105}
@@ -176,7 +185,7 @@ async function fetchAccountCount (url: string) {
176 try { 185 try {
177 requestResult = await doRequest(options) 186 requestResult = await doRequest(options)
178 } catch (err) { 187 } catch (err) {
179 logger.warning('Cannot fetch remote account count %s.', url, err) 188 logger.warn('Cannot fetch remote account count %s.', url, err)
180 return undefined 189 return undefined
181 } 190 }
182 191
diff --git a/server/helpers/custom-validators/video-accounts.ts b/server/helpers/custom-validators/accounts.ts
index 31808ae1e..6d6219a95 100644
--- a/server/helpers/custom-validators/video-accounts.ts
+++ b/server/helpers/custom-validators/accounts.ts
@@ -10,14 +10,14 @@ import { logger } from '../logger'
10import { isUserUsernameValid } from './users' 10import { isUserUsernameValid } from './users'
11import { isHostValid } from './pods' 11import { isHostValid } from './pods'
12 12
13function isVideoAccountNameValid (value: string) { 13function isAccountNameValid (value: string) {
14 return isUserUsernameValid(value) 14 return isUserUsernameValid(value)
15} 15}
16 16
17function isAccountNameWithHostValid (value: string) { 17function isAccountNameWithHostValid (value: string) {
18 const [ name, host ] = value.split('@') 18 const [ name, host ] = value.split('@')
19 19
20 return isVideoAccountNameValid(name) && isHostValid(host) 20 return isAccountNameValid(name) && isHostValid(host)
21} 21}
22 22
23function checkVideoAccountExists (id: string, res: express.Response, callback: () => void) { 23function checkVideoAccountExists (id: string, res: express.Response, callback: () => void) {
@@ -38,10 +38,10 @@ function checkVideoAccountExists (id: string, res: express.Response, callback: (
38 res.locals.account = account 38 res.locals.account = account
39 callback() 39 callback()
40 }) 40 })
41 .catch(err => { 41 .catch(err => {
42 logger.error('Error in video account request validator.', err) 42 logger.error('Error in video account request validator.', err)
43 return res.sendStatus(500) 43 return res.sendStatus(500)
44 }) 44 })
45} 45}
46 46
47// --------------------------------------------------------------------------- 47// ---------------------------------------------------------------------------
@@ -49,5 +49,5 @@ function checkVideoAccountExists (id: string, res: express.Response, callback: (
49export { 49export {
50 checkVideoAccountExists, 50 checkVideoAccountExists,
51 isAccountNameWithHostValid, 51 isAccountNameWithHostValid,
52 isVideoAccountNameValid 52 isAccountNameValid
53} 53}
diff --git a/server/helpers/custom-validators/activitypub/account.ts b/server/helpers/custom-validators/activitypub/account.ts
index acd2b8058..645f55a5a 100644
--- a/server/helpers/custom-validators/activitypub/account.ts
+++ b/server/helpers/custom-validators/activitypub/account.ts
@@ -1,9 +1,8 @@
1import * as validator from 'validator' 1import * as validator from 'validator'
2
3import { exists, isUUIDValid } from '../misc'
4import { isActivityPubUrlValid } from './misc'
5import { isUserUsernameValid } from '../users'
6import { CONSTRAINTS_FIELDS } from '../../../initializers/constants' 2import { CONSTRAINTS_FIELDS } from '../../../initializers/constants'
3import { isAccountNameValid } from '../accounts'
4import { exists, isUUIDValid } from '../misc'
5import { isActivityPubUrlValid, isBaseActivityValid } from './misc'
7 6
8function isAccountEndpointsObjectValid (endpointObject: any) { 7function isAccountEndpointsObjectValid (endpointObject: any) {
9 return isAccountSharedInboxValid(endpointObject.sharedInbox) 8 return isAccountSharedInboxValid(endpointObject.sharedInbox)
@@ -59,10 +58,6 @@ function isAccountOutboxValid (outbox: string) {
59 return isActivityPubUrlValid(outbox) 58 return isActivityPubUrlValid(outbox)
60} 59}
61 60
62function isAccountNameValid (name: string) {
63 return isUserUsernameValid(name)
64}
65
66function isAccountPreferredUsernameValid (preferredUsername: string) { 61function isAccountPreferredUsernameValid (preferredUsername: string) {
67 return isAccountNameValid(preferredUsername) 62 return isAccountNameValid(preferredUsername)
68} 63}
@@ -90,7 +85,7 @@ function isRemoteAccountValid (remoteAccount: any) {
90 isAccountPreferredUsernameValid(remoteAccount.preferredUsername) && 85 isAccountPreferredUsernameValid(remoteAccount.preferredUsername) &&
91 isAccountUrlValid(remoteAccount.url) && 86 isAccountUrlValid(remoteAccount.url) &&
92 isAccountPublicKeyObjectValid(remoteAccount.publicKey) && 87 isAccountPublicKeyObjectValid(remoteAccount.publicKey) &&
93 isAccountEndpointsObjectValid(remoteAccount.endpoint) 88 isAccountEndpointsObjectValid(remoteAccount.endpoints)
94} 89}
95 90
96function isAccountFollowingCountValid (value: string) { 91function isAccountFollowingCountValid (value: string) {
@@ -101,6 +96,19 @@ function isAccountFollowersCountValid (value: string) {
101 return exists(value) && validator.isInt('' + value, { min: 0 }) 96 return exists(value) && validator.isInt('' + value, { min: 0 })
102} 97}
103 98
99function isAccountDeleteActivityValid (activity: any) {
100 return isBaseActivityValid(activity, 'Delete')
101}
102
103function isAccountFollowActivityValid (activity: any) {
104 return isBaseActivityValid(activity, 'Follow') &&
105 isActivityPubUrlValid(activity.object)
106}
107
108function isAccountAcceptActivityValid (activity: any) {
109 return isBaseActivityValid(activity, 'Accept')
110}
111
104// --------------------------------------------------------------------------- 112// ---------------------------------------------------------------------------
105 113
106export { 114export {
@@ -122,5 +130,8 @@ export {
122 isRemoteAccountValid, 130 isRemoteAccountValid,
123 isAccountFollowingCountValid, 131 isAccountFollowingCountValid,
124 isAccountFollowersCountValid, 132 isAccountFollowersCountValid,
125 isAccountNameValid 133 isAccountNameValid,
134 isAccountFollowActivityValid,
135 isAccountAcceptActivityValid,
136 isAccountDeleteActivityValid
126} 137}
diff --git a/server/helpers/custom-validators/activitypub/activity.ts b/server/helpers/custom-validators/activitypub/activity.ts
index dd671c4cf..b5ba0f7af 100644
--- a/server/helpers/custom-validators/activitypub/activity.ts
+++ b/server/helpers/custom-validators/activitypub/activity.ts
@@ -1,9 +1,13 @@
1import * as validator from 'validator' 1import * as validator from 'validator'
2import { isAccountAcceptActivityValid, isAccountDeleteActivityValid, isAccountFollowActivityValid } from './account'
3import { isActivityPubUrlValid } from './misc'
2import { 4import {
3 isVideoChannelCreateActivityValid, 5 isVideoChannelCreateActivityValid,
6 isVideoChannelDeleteActivityValid,
7 isVideoChannelUpdateActivityValid,
4 isVideoTorrentAddActivityValid, 8 isVideoTorrentAddActivityValid,
5 isVideoTorrentUpdateActivityValid, 9 isVideoTorrentDeleteActivityValid,
6 isVideoChannelUpdateActivityValid 10 isVideoTorrentUpdateActivityValid
7} from './videos' 11} from './videos'
8 12
9function isRootActivityValid (activity: any) { 13function isRootActivityValid (activity: any) {
@@ -14,8 +18,8 @@ function isRootActivityValid (activity: any) {
14 Array.isArray(activity.items) 18 Array.isArray(activity.items)
15 ) || 19 ) ||
16 ( 20 (
17 validator.isURL(activity.id) && 21 isActivityPubUrlValid(activity.id) &&
18 validator.isURL(activity.actor) 22 isActivityPubUrlValid(activity.actor)
19 ) 23 )
20} 24}
21 25
@@ -23,7 +27,12 @@ function isActivityValid (activity: any) {
23 return isVideoTorrentAddActivityValid(activity) || 27 return isVideoTorrentAddActivityValid(activity) ||
24 isVideoChannelCreateActivityValid(activity) || 28 isVideoChannelCreateActivityValid(activity) ||
25 isVideoTorrentUpdateActivityValid(activity) || 29 isVideoTorrentUpdateActivityValid(activity) ||
26 isVideoChannelUpdateActivityValid(activity) 30 isVideoChannelUpdateActivityValid(activity) ||
31 isVideoTorrentDeleteActivityValid(activity) ||
32 isVideoChannelDeleteActivityValid(activity) ||
33 isAccountDeleteActivityValid(activity) ||
34 isAccountFollowActivityValid(activity) ||
35 isAccountAcceptActivityValid(activity)
27} 36}
28 37
29// --------------------------------------------------------------------------- 38// ---------------------------------------------------------------------------
diff --git a/server/helpers/custom-validators/activitypub/misc.ts b/server/helpers/custom-validators/activitypub/misc.ts
index a94c36b51..665a63a73 100644
--- a/server/helpers/custom-validators/activitypub/misc.ts
+++ b/server/helpers/custom-validators/activitypub/misc.ts
@@ -23,10 +23,12 @@ function isActivityPubUrlValid (url: string) {
23function isBaseActivityValid (activity: any, type: string) { 23function isBaseActivityValid (activity: any, type: string) {
24 return Array.isArray(activity['@context']) && 24 return Array.isArray(activity['@context']) &&
25 activity.type === type && 25 activity.type === type &&
26 validator.isURL(activity.id) && 26 isActivityPubUrlValid(activity.id) &&
27 validator.isURL(activity.actor) && 27 isActivityPubUrlValid(activity.actor) &&
28 Array.isArray(activity.to) && 28 (
29 activity.to.every(t => validator.isURL(t)) 29 activity.to === undefined ||
30 (Array.isArray(activity.to) && activity.to.every(t => isActivityPubUrlValid(t)))
31 )
30} 32}
31 33
32export { 34export {
diff --git a/server/helpers/custom-validators/activitypub/videos.ts b/server/helpers/custom-validators/activitypub/videos.ts
index 8f6d50f50..c9ecf1f3d 100644
--- a/server/helpers/custom-validators/activitypub/videos.ts
+++ b/server/helpers/custom-validators/activitypub/videos.ts
@@ -14,7 +14,7 @@ import {
14 isVideoUrlValid 14 isVideoUrlValid
15} from '../videos' 15} from '../videos'
16import { isVideoChannelDescriptionValid, isVideoChannelNameValid } from '../video-channels' 16import { isVideoChannelDescriptionValid, isVideoChannelNameValid } from '../video-channels'
17import { isBaseActivityValid } from './misc' 17import { isActivityPubUrlValid, isBaseActivityValid } from './misc'
18 18
19function isVideoTorrentAddActivityValid (activity: any) { 19function isVideoTorrentAddActivityValid (activity: any) {
20 return isBaseActivityValid(activity, 'Add') && 20 return isBaseActivityValid(activity, 'Add') &&
@@ -26,6 +26,10 @@ function isVideoTorrentUpdateActivityValid (activity: any) {
26 isVideoTorrentObjectValid(activity.object) 26 isVideoTorrentObjectValid(activity.object)
27} 27}
28 28
29function isVideoTorrentDeleteActivityValid (activity: any) {
30 return isBaseActivityValid(activity, 'Delete')
31}
32
29function isVideoTorrentObjectValid (video: any) { 33function isVideoTorrentObjectValid (video: any) {
30 return video.type === 'Video' && 34 return video.type === 'Video' &&
31 isVideoNameValid(video.name) && 35 isVideoNameValid(video.name) &&
@@ -54,6 +58,10 @@ function isVideoChannelUpdateActivityValid (activity: any) {
54 isVideoChannelObjectValid(activity.object) 58 isVideoChannelObjectValid(activity.object)
55} 59}
56 60
61function isVideoChannelDeleteActivityValid (activity: any) {
62 return isBaseActivityValid(activity, 'Delete')
63}
64
57function isVideoChannelObjectValid (videoChannel: any) { 65function isVideoChannelObjectValid (videoChannel: any) {
58 return videoChannel.type === 'VideoChannel' && 66 return videoChannel.type === 'VideoChannel' &&
59 isVideoChannelNameValid(videoChannel.name) && 67 isVideoChannelNameValid(videoChannel.name) &&
@@ -67,7 +75,9 @@ export {
67 isVideoTorrentAddActivityValid, 75 isVideoTorrentAddActivityValid,
68 isVideoChannelCreateActivityValid, 76 isVideoChannelCreateActivityValid,
69 isVideoTorrentUpdateActivityValid, 77 isVideoTorrentUpdateActivityValid,
70 isVideoChannelUpdateActivityValid 78 isVideoChannelUpdateActivityValid,
79 isVideoChannelDeleteActivityValid,
80 isVideoTorrentDeleteActivityValid
71} 81}
72 82
73// --------------------------------------------------------------------------- 83// ---------------------------------------------------------------------------
diff --git a/server/helpers/custom-validators/index.ts b/server/helpers/custom-validators/index.ts
index 33922b8fe..1c475e301 100644
--- a/server/helpers/custom-validators/index.ts
+++ b/server/helpers/custom-validators/index.ts
@@ -3,6 +3,7 @@ export * from './misc'
3export * from './pods' 3export * from './pods'
4export * from './pods' 4export * from './pods'
5export * from './users' 5export * from './users'
6export * from './video-accounts' 6export * from './accounts'
7export * from './video-channels' 7export * from './video-channels'
8export * from './videos' 8export * from './videos'
9export * from './webfinger'
diff --git a/server/helpers/custom-validators/webfinger.ts b/server/helpers/custom-validators/webfinger.ts
new file mode 100644
index 000000000..e93115d81
--- /dev/null
+++ b/server/helpers/custom-validators/webfinger.ts
@@ -0,0 +1,25 @@
1import 'express-validator'
2import 'multer'
3import { CONFIG } from '../../initializers/constants'
4import { exists } from './misc'
5
6function isWebfingerResourceValid (value: string) {
7 if (!exists(value)) return false
8 if (value.startsWith('acct:') === false) return false
9
10 const accountWithHost = value.substr(5)
11 const accountParts = accountWithHost.split('@')
12 if (accountParts.length !== 2) return false
13
14 const host = accountParts[1]
15
16 if (host !== CONFIG.WEBSERVER.HOST) return false
17
18 return true
19}
20
21// ---------------------------------------------------------------------------
22
23export {
24 isWebfingerResourceValid
25}
diff --git a/server/helpers/webfinger.ts b/server/helpers/webfinger.ts
index 164ae4951..0155e5f3e 100644
--- a/server/helpers/webfinger.ts
+++ b/server/helpers/webfinger.ts
@@ -12,17 +12,20 @@ const webfinger = new WebFinger({
12 request_timeout: 3000 12 request_timeout: 3000
13}) 13})
14 14
15async function getAccountFromWebfinger (url: string) { 15async function getAccountFromWebfinger (nameWithHost: string) {
16 const webfingerData: WebFingerData = await webfingerLookup(url) 16 const webfingerData: WebFingerData = await webfingerLookup(nameWithHost)
17 17
18 if (Array.isArray(webfingerData.links) === false) return undefined 18 if (Array.isArray(webfingerData.links) === false) throw new Error('WebFinger links is not an array.')
19 19
20 const selfLink = webfingerData.links.find(l => l.rel === 'self') 20 const selfLink = webfingerData.links.find(l => l.rel === 'self')
21 if (selfLink === undefined || isActivityPubUrlValid(selfLink.href) === false) return undefined 21 if (selfLink === undefined || isActivityPubUrlValid(selfLink.href) === false) {
22 throw new Error('Cannot find self link or href is not a valid URL.')
23 }
22 24
23 const { account } = await fetchRemoteAccountAndCreatePod(selfLink.href) 25 const res = await fetchRemoteAccountAndCreatePod(selfLink.href)
26 if (res === undefined) throw new Error('Cannot fetch and create pod of remote account ' + selfLink.href)
24 27
25 return account 28 return res.account
26} 29}
27 30
28// --------------------------------------------------------------------------- 31// ---------------------------------------------------------------------------
@@ -33,12 +36,12 @@ export {
33 36
34// --------------------------------------------------------------------------- 37// ---------------------------------------------------------------------------
35 38
36function webfingerLookup (url: string) { 39function webfingerLookup (nameWithHost: string) {
37 return new Promise<WebFingerData>((res, rej) => { 40 return new Promise<WebFingerData>((res, rej) => {
38 webfinger.lookup(url, (err, p) => { 41 webfinger.lookup(nameWithHost, (err, p) => {
39 if (err) return rej(err) 42 if (err) return rej(err)
40 43
41 return p 44 return res(p.object)
42 }) 45 })
43 }) 46 })
44} 47}