@evanp/activitypub-bot 0.12.1 → 0.13.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/app.js +19 -1
- package/lib/index.js +2 -0
- package/lib/routes/collection.js +83 -67
- package/lib/routes/inbox.js +8 -0
- package/lib/routes/object.js +25 -5
- package/package.json +3 -3
- package/.github/dependabot.yml +0 -11
- package/.github/workflows/main.yml +0 -34
- package/.github/workflows/tag.yml +0 -106
- package/.nvmrc +0 -1
- package/Dockerfile +0 -17
- package/docs/activitypub.bot.drawio +0 -110
- package/tests/activitydistributor.test.js +0 -606
- package/tests/activityhandler.test.js +0 -2276
- package/tests/activitypubclient.test.js +0 -210
- package/tests/actorstorage.test.js +0 -283
- package/tests/app.test.js +0 -17
- package/tests/authorizer.test.js +0 -301
- package/tests/bot.donothing.test.js +0 -30
- package/tests/bot.ok.test.js +0 -101
- package/tests/botcontext.test.js +0 -720
- package/tests/botdatastorage.test.js +0 -88
- package/tests/botfactory.provincebotfactory.test.js +0 -430
- package/tests/digester.test.js +0 -56
- package/tests/fixtures/bots.js +0 -27
- package/tests/fixtures/eventloggingbot.js +0 -57
- package/tests/fixtures/provincebotfactory.js +0 -53
- package/tests/httpsignature.test.js +0 -199
- package/tests/httpsignatureauthenticator.test.js +0 -463
- package/tests/index.test.js +0 -10
- package/tests/keystorage.test.js +0 -124
- package/tests/microsyntax.test.js +0 -123
- package/tests/objectcache.test.js +0 -133
- package/tests/objectstorage.test.js +0 -149
- package/tests/remotekeystorage.test.js +0 -78
- package/tests/routes.actor.test.js +0 -214
- package/tests/routes.collection.test.js +0 -433
- package/tests/routes.health.test.js +0 -41
- package/tests/routes.inbox.test.js +0 -136
- package/tests/routes.object.test.js +0 -525
- package/tests/routes.server.test.js +0 -69
- package/tests/routes.sharedinbox.test.js +0 -473
- package/tests/routes.webfinger.test.js +0 -68
- package/tests/urlformatter.test.js +0 -164
- package/tests/utils/digest.js +0 -7
- package/tests/utils/nock.js +0 -499
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import BotFactory from '../../lib/botfactory.js'
|
|
2
|
-
import Bot from '../../lib/bot.js'
|
|
3
|
-
|
|
4
|
-
class ProvinceBot extends Bot {
|
|
5
|
-
#name
|
|
6
|
-
#type
|
|
7
|
-
|
|
8
|
-
constructor (username, name, type) {
|
|
9
|
-
super(username)
|
|
10
|
-
this.#name = name
|
|
11
|
-
this.#type = type
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
get fullname () {
|
|
15
|
-
return this.#name
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
get description () {
|
|
19
|
-
return `The ${this.#type} of ${this.#name}`
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export default class ProvinceBotFactory extends BotFactory {
|
|
24
|
-
static #provinces = {
|
|
25
|
-
ab: ['Alberta', 'province'],
|
|
26
|
-
on: ['Ontario', 'province'],
|
|
27
|
-
qc: ['Quebec', 'province'],
|
|
28
|
-
bc: ['British Columbia', 'province'],
|
|
29
|
-
mb: ['Manitoba', 'province'],
|
|
30
|
-
sk: ['Saskatchewan', 'province'],
|
|
31
|
-
nb: ['New Brunswick', 'province'],
|
|
32
|
-
ns: ['Nova Scotia', 'province'],
|
|
33
|
-
pe: ['Prince Edward Island', 'province'],
|
|
34
|
-
nl: ['Newfoundland and Labrador', 'province'],
|
|
35
|
-
nu: ['Nunavut', 'territory'],
|
|
36
|
-
nt: ['Northwest Territories', 'territory'],
|
|
37
|
-
yt: ['Yukon', 'territory']
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
async canCreate (username) {
|
|
41
|
-
return (username in ProvinceBotFactory.#provinces)
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
async create (username) {
|
|
45
|
-
if (!(username in ProvinceBotFactory.#provinces)) {
|
|
46
|
-
throw new Error(`cannot create a bot with username ${username}`)
|
|
47
|
-
}
|
|
48
|
-
const [name, type] = ProvinceBotFactory.#provinces[username]
|
|
49
|
-
const bot = new ProvinceBot(username, name, type)
|
|
50
|
-
await bot.initialize(await this._context.duplicate(username))
|
|
51
|
-
return bot
|
|
52
|
-
}
|
|
53
|
-
}
|
|
@@ -1,199 +0,0 @@
|
|
|
1
|
-
import { describe, before, after, it } from 'node:test'
|
|
2
|
-
import assert from 'node:assert'
|
|
3
|
-
import { Sequelize } from 'sequelize'
|
|
4
|
-
import { nockSetup, nockSignature, nockKeyRotate, getPublicKey, getPrivateKey, nockFormat } from './utils/nock.js'
|
|
5
|
-
import { HTTPSignature } from '../lib/httpsignature.js'
|
|
6
|
-
import Logger from 'pino'
|
|
7
|
-
import { Digester } from '../lib/digester.js'
|
|
8
|
-
import { runMigrations } from '../lib/migrations/index.js'
|
|
9
|
-
|
|
10
|
-
describe('HTTPSignature', async () => {
|
|
11
|
-
const domain = 'activitypubbot.example'
|
|
12
|
-
const origin = `https://${domain}`
|
|
13
|
-
let connection = null
|
|
14
|
-
let httpSignature = null
|
|
15
|
-
let logger = null
|
|
16
|
-
let digester = null
|
|
17
|
-
before(async () => {
|
|
18
|
-
logger = Logger({
|
|
19
|
-
level: 'silent'
|
|
20
|
-
})
|
|
21
|
-
connection = new Sequelize({ dialect: 'sqlite', storage: ':memory:', logging: false })
|
|
22
|
-
await connection.authenticate()
|
|
23
|
-
await runMigrations(connection)
|
|
24
|
-
nockSetup('social.example')
|
|
25
|
-
digester = new Digester(logger)
|
|
26
|
-
})
|
|
27
|
-
|
|
28
|
-
after(async () => {
|
|
29
|
-
await connection.close()
|
|
30
|
-
digester = null
|
|
31
|
-
})
|
|
32
|
-
|
|
33
|
-
it('can initialize', async () => {
|
|
34
|
-
httpSignature = new HTTPSignature(logger)
|
|
35
|
-
assert.ok(httpSignature)
|
|
36
|
-
})
|
|
37
|
-
|
|
38
|
-
it('can validate a signature', async () => {
|
|
39
|
-
const username = 'test'
|
|
40
|
-
const date = new Date().toUTCString()
|
|
41
|
-
const signature = await nockSignature({
|
|
42
|
-
url: `${origin}/user/ok/outbox`,
|
|
43
|
-
date,
|
|
44
|
-
username
|
|
45
|
-
})
|
|
46
|
-
const headers = {
|
|
47
|
-
date,
|
|
48
|
-
signature,
|
|
49
|
-
host: URL.parse(origin).host
|
|
50
|
-
}
|
|
51
|
-
const publicKeyPem = await getPublicKey(username)
|
|
52
|
-
const method = 'GET'
|
|
53
|
-
const path = '/user/ok/outbox'
|
|
54
|
-
const result = await httpSignature.validate(publicKeyPem, signature, method, path, headers)
|
|
55
|
-
assert.ok(result)
|
|
56
|
-
})
|
|
57
|
-
|
|
58
|
-
it('can validate a signature on an URL with parameters', async () => {
|
|
59
|
-
const username = 'test'
|
|
60
|
-
const lname = 'ok'
|
|
61
|
-
const date = new Date().toUTCString()
|
|
62
|
-
const signature = await nockSignature({
|
|
63
|
-
url: `${origin}/.well-known/webfinger?resource=acct:${lname}@${domain}`,
|
|
64
|
-
date,
|
|
65
|
-
username
|
|
66
|
-
})
|
|
67
|
-
const headers = {
|
|
68
|
-
date,
|
|
69
|
-
signature,
|
|
70
|
-
host: URL.parse(origin).host
|
|
71
|
-
}
|
|
72
|
-
const publicKeyPem = await getPublicKey(username)
|
|
73
|
-
const method = 'GET'
|
|
74
|
-
const path = `/.well-known/webfinger?resource=acct:${lname}@${domain}`
|
|
75
|
-
const result = await httpSignature.validate(publicKeyPem, signature, method, path, headers)
|
|
76
|
-
assert.ok(result)
|
|
77
|
-
})
|
|
78
|
-
|
|
79
|
-
it('can handle key rotation', async () => {
|
|
80
|
-
const username = 'rotate'
|
|
81
|
-
const date = new Date().toUTCString()
|
|
82
|
-
const signature = await nockSignature({
|
|
83
|
-
url: `${origin}/user/ok/outbox`,
|
|
84
|
-
date,
|
|
85
|
-
username
|
|
86
|
-
})
|
|
87
|
-
const headers = {
|
|
88
|
-
date,
|
|
89
|
-
signature,
|
|
90
|
-
host: URL.parse(origin).host
|
|
91
|
-
}
|
|
92
|
-
const publicKeyPem = await getPublicKey(username)
|
|
93
|
-
const method = 'GET'
|
|
94
|
-
const path = '/user/ok/outbox'
|
|
95
|
-
await httpSignature.validate(publicKeyPem, signature, method, path, headers)
|
|
96
|
-
await nockKeyRotate(username)
|
|
97
|
-
const signature2 = await nockSignature({
|
|
98
|
-
url: `${origin}/user/ok/outbox`,
|
|
99
|
-
date,
|
|
100
|
-
username
|
|
101
|
-
})
|
|
102
|
-
const headers2 = {
|
|
103
|
-
date,
|
|
104
|
-
signature,
|
|
105
|
-
host: URL.parse(origin).host
|
|
106
|
-
}
|
|
107
|
-
const publicKeyPem2 = await getPublicKey(username)
|
|
108
|
-
assert.notStrictEqual(publicKeyPem, publicKeyPem2)
|
|
109
|
-
const result2 = await httpSignature.validate(publicKeyPem2, signature2, method, path, headers2)
|
|
110
|
-
assert.ok(result2)
|
|
111
|
-
})
|
|
112
|
-
|
|
113
|
-
it('can sign a GET request', async () => {
|
|
114
|
-
const date = new Date().toUTCString()
|
|
115
|
-
const headers = {
|
|
116
|
-
Date: date,
|
|
117
|
-
Host: URL.parse(origin).host,
|
|
118
|
-
'X-Unused-Header': 'test',
|
|
119
|
-
Accept: 'application/activity+json',
|
|
120
|
-
'User-Agent': 'activitypubbot-test/0.0.1'
|
|
121
|
-
}
|
|
122
|
-
const privateKey = await getPrivateKey('test')
|
|
123
|
-
const method = 'GET'
|
|
124
|
-
const url = nockFormat({ username: 'test', obj: 'outbox' })
|
|
125
|
-
const keyId = nockFormat({ username: 'test', key: true })
|
|
126
|
-
const signature = await httpSignature.sign({ privateKey, keyId, url, method, headers })
|
|
127
|
-
assert.ok(signature)
|
|
128
|
-
assert.match(signature, /^keyId="https:\/\/social\.example\/user\/test\/publickey",headers="\(request-target\) host date user-agent accept",signature=".*",algorithm="rsa-sha256"$/)
|
|
129
|
-
})
|
|
130
|
-
|
|
131
|
-
it('can sign a POST request', async () => {
|
|
132
|
-
const body = JSON.stringify({
|
|
133
|
-
'@context': 'https://www.w3.org/ns/activitystreams',
|
|
134
|
-
type: 'Create',
|
|
135
|
-
actor: nockFormat({ username: 'test' }),
|
|
136
|
-
object: nockFormat({ username: 'test', obj: 'note', num: 1 })
|
|
137
|
-
})
|
|
138
|
-
const headers = {
|
|
139
|
-
date: new Date().toUTCString(),
|
|
140
|
-
host: URL.parse(origin).host,
|
|
141
|
-
digest: await digester.digest(body),
|
|
142
|
-
'content-type': 'application/activity+json',
|
|
143
|
-
'User-Agent': 'activitypubbot-test/0.0.1'
|
|
144
|
-
}
|
|
145
|
-
const privateKey = await getPrivateKey('test')
|
|
146
|
-
const method = 'POST'
|
|
147
|
-
const url = nockFormat({ username: 'test', obj: 'outbox' })
|
|
148
|
-
const keyId = nockFormat({ username: 'test', key: true })
|
|
149
|
-
const signature = await httpSignature.sign({ privateKey, keyId, url, method, headers })
|
|
150
|
-
assert.ok(signature)
|
|
151
|
-
assert.match(signature, /^keyId="https:\/\/social\.example\/user\/test\/publickey",headers="\(request-target\) host date user-agent content-type digest",signature=".*",algorithm="rsa-sha256"$/)
|
|
152
|
-
})
|
|
153
|
-
|
|
154
|
-
it('errors if required GET headers not present', async () => {
|
|
155
|
-
const date = new Date().toUTCString()
|
|
156
|
-
const headers = {
|
|
157
|
-
Date: date,
|
|
158
|
-
Host: URL.parse(origin).host,
|
|
159
|
-
'User-Agent': 'activitypubbot-test/0.0.1'
|
|
160
|
-
}
|
|
161
|
-
const privateKey = await getPrivateKey('test')
|
|
162
|
-
const method = 'GET'
|
|
163
|
-
const url = nockFormat({ username: 'test', obj: 'outbox' })
|
|
164
|
-
const keyId = nockFormat({ username: 'test', key: true })
|
|
165
|
-
try {
|
|
166
|
-
await httpSignature.sign({ privateKey, keyId, url, method, headers })
|
|
167
|
-
assert.fail('Expected error not thrown')
|
|
168
|
-
} catch (err) {
|
|
169
|
-
assert.equal(err.name, 'Error')
|
|
170
|
-
assert.equal(err.message, 'Missing header: accept')
|
|
171
|
-
}
|
|
172
|
-
})
|
|
173
|
-
|
|
174
|
-
it('errors if required POST headers not present', async () => {
|
|
175
|
-
const body = JSON.stringify({
|
|
176
|
-
'@context': 'https://www.w3.org/ns/activitystreams',
|
|
177
|
-
type: 'Create',
|
|
178
|
-
actor: nockFormat({ username: 'test' }),
|
|
179
|
-
object: nockFormat({ username: 'test', obj: 'note', num: 1 })
|
|
180
|
-
})
|
|
181
|
-
const headers = {
|
|
182
|
-
date: new Date().toUTCString(),
|
|
183
|
-
host: URL.parse(origin).host,
|
|
184
|
-
digest: await digester.digest(body),
|
|
185
|
-
'User-Agent': 'activitypubbot-test/0.0.1'
|
|
186
|
-
}
|
|
187
|
-
const privateKey = await getPrivateKey('test')
|
|
188
|
-
const method = 'POST'
|
|
189
|
-
const url = nockFormat({ username: 'test', obj: 'outbox' })
|
|
190
|
-
const keyId = nockFormat({ username: 'test', key: true })
|
|
191
|
-
try {
|
|
192
|
-
await httpSignature.sign({ privateKey, keyId, url, method, headers })
|
|
193
|
-
assert.fail('Expected error not thrown')
|
|
194
|
-
} catch (err) {
|
|
195
|
-
assert.equal(err.name, 'Error')
|
|
196
|
-
assert.equal(err.message, 'Missing header: content-type')
|
|
197
|
-
}
|
|
198
|
-
})
|
|
199
|
-
})
|
|
@@ -1,463 +0,0 @@
|
|
|
1
|
-
import { describe, before, after, it } from 'node:test'
|
|
2
|
-
import assert from 'node:assert'
|
|
3
|
-
import { Sequelize } from 'sequelize'
|
|
4
|
-
import { KeyStorage } from '../lib/keystorage.js'
|
|
5
|
-
import { nockSetup, nockSignature, nockKeyRotate, nockFormat } from './utils/nock.js'
|
|
6
|
-
import { HTTPSignature } from '../lib/httpsignature.js'
|
|
7
|
-
import { HTTPSignatureAuthenticator } from '../lib/httpsignatureauthenticator.js'
|
|
8
|
-
import Logger from 'pino'
|
|
9
|
-
import { Digester } from '../lib/digester.js'
|
|
10
|
-
import { RemoteKeyStorage } from '../lib/remotekeystorage.js'
|
|
11
|
-
import { ActivityPubClient } from '../lib/activitypubclient.js'
|
|
12
|
-
import { UrlFormatter } from '../lib/urlformatter.js'
|
|
13
|
-
import as2 from '../lib/activitystreams.js'
|
|
14
|
-
import { runMigrations } from '../lib/migrations/index.js'
|
|
15
|
-
|
|
16
|
-
describe('HTTPSignatureAuthenticator', async () => {
|
|
17
|
-
const domain = 'activitypubbot.example'
|
|
18
|
-
const origin = `https://${domain}`
|
|
19
|
-
let authenticator = null
|
|
20
|
-
let logger = null
|
|
21
|
-
let signer = null
|
|
22
|
-
let digester = null
|
|
23
|
-
let remoteKeyStorage = null
|
|
24
|
-
let connection = null
|
|
25
|
-
const next = (err) => {
|
|
26
|
-
if (err) {
|
|
27
|
-
assert.fail(`Failed to authenticate: ${err.message}`)
|
|
28
|
-
} else {
|
|
29
|
-
assert.ok(true, 'Authenticated successfully')
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
const failNext = (err) => {
|
|
33
|
-
if (err) {
|
|
34
|
-
assert.ok(true, 'Failed successfully')
|
|
35
|
-
} else {
|
|
36
|
-
assert.fail('Passed through an incorrect request')
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
before(async () => {
|
|
40
|
-
logger = Logger({
|
|
41
|
-
level: 'silent'
|
|
42
|
-
})
|
|
43
|
-
connection = new Sequelize({ dialect: 'sqlite', storage: ':memory:', logging: false })
|
|
44
|
-
await connection.authenticate()
|
|
45
|
-
await runMigrations(connection)
|
|
46
|
-
signer = new HTTPSignature(logger)
|
|
47
|
-
digester = new Digester(logger)
|
|
48
|
-
const formatter = new UrlFormatter(origin)
|
|
49
|
-
const keyStorage = new KeyStorage(connection, logger)
|
|
50
|
-
const client = new ActivityPubClient(keyStorage, formatter, signer, digester, logger)
|
|
51
|
-
remoteKeyStorage = new RemoteKeyStorage(client, connection, logger)
|
|
52
|
-
nockSetup('social.example')
|
|
53
|
-
})
|
|
54
|
-
|
|
55
|
-
after(async () => {
|
|
56
|
-
await connection.close()
|
|
57
|
-
authenticator = null
|
|
58
|
-
digester = null
|
|
59
|
-
signer = null
|
|
60
|
-
remoteKeyStorage = null
|
|
61
|
-
})
|
|
62
|
-
|
|
63
|
-
it('can initialize', async () => {
|
|
64
|
-
authenticator = new HTTPSignatureAuthenticator(
|
|
65
|
-
remoteKeyStorage,
|
|
66
|
-
signer,
|
|
67
|
-
digester,
|
|
68
|
-
logger)
|
|
69
|
-
})
|
|
70
|
-
|
|
71
|
-
it('can authenticate a valid GET request', async () => {
|
|
72
|
-
const username = 'test'
|
|
73
|
-
const date = new Date().toUTCString()
|
|
74
|
-
const signature = await nockSignature({
|
|
75
|
-
url: `${origin}/user/ok/outbox`,
|
|
76
|
-
date,
|
|
77
|
-
username
|
|
78
|
-
})
|
|
79
|
-
const headers = {
|
|
80
|
-
date,
|
|
81
|
-
signature,
|
|
82
|
-
host: URL.parse(origin).host
|
|
83
|
-
}
|
|
84
|
-
const method = 'GET'
|
|
85
|
-
const originalUrl = '/user/ok/outbox'
|
|
86
|
-
const res = {
|
|
87
|
-
|
|
88
|
-
}
|
|
89
|
-
const req = {
|
|
90
|
-
headers,
|
|
91
|
-
originalUrl,
|
|
92
|
-
method,
|
|
93
|
-
get: function (name) {
|
|
94
|
-
return this.headers[name.toLowerCase()]
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
await authenticator.authenticate(req, res, next)
|
|
98
|
-
})
|
|
99
|
-
|
|
100
|
-
it('can authenticate a valid GET request with parameters', async () => {
|
|
101
|
-
const lname = 'ok'
|
|
102
|
-
const username = 'test'
|
|
103
|
-
const date = new Date().toUTCString()
|
|
104
|
-
const signature = await nockSignature({
|
|
105
|
-
url: `${origin}/.well-known/webfinger?resource=acct:${lname}@${domain}`,
|
|
106
|
-
date,
|
|
107
|
-
username
|
|
108
|
-
})
|
|
109
|
-
const headers = {
|
|
110
|
-
date,
|
|
111
|
-
signature,
|
|
112
|
-
host: URL.parse(origin).host
|
|
113
|
-
}
|
|
114
|
-
const method = 'GET'
|
|
115
|
-
const originalUrl = `/.well-known/webfinger?resource=acct:${lname}@${domain}`
|
|
116
|
-
const res = {
|
|
117
|
-
|
|
118
|
-
}
|
|
119
|
-
const req = {
|
|
120
|
-
headers,
|
|
121
|
-
originalUrl,
|
|
122
|
-
method,
|
|
123
|
-
get: function (name) {
|
|
124
|
-
return this.headers[name.toLowerCase()]
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
await authenticator.authenticate(req, res, next)
|
|
128
|
-
})
|
|
129
|
-
|
|
130
|
-
it('can authenticate a valid GET request after key rotation', async () => {
|
|
131
|
-
const username = 'test2'
|
|
132
|
-
const date = new Date().toUTCString()
|
|
133
|
-
const signature = await nockSignature({
|
|
134
|
-
url: `${origin}/user/ok/outbox`,
|
|
135
|
-
date,
|
|
136
|
-
username
|
|
137
|
-
})
|
|
138
|
-
const headers = {
|
|
139
|
-
date,
|
|
140
|
-
signature,
|
|
141
|
-
host: URL.parse(origin).host
|
|
142
|
-
}
|
|
143
|
-
const method = 'GET'
|
|
144
|
-
const originalUrl = '/user/ok/outbox'
|
|
145
|
-
const res = {
|
|
146
|
-
|
|
147
|
-
}
|
|
148
|
-
const req = {
|
|
149
|
-
headers,
|
|
150
|
-
originalUrl,
|
|
151
|
-
method,
|
|
152
|
-
get: function (name) {
|
|
153
|
-
return this.headers[name.toLowerCase()]
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
await authenticator.authenticate(req, res, next)
|
|
157
|
-
await nockKeyRotate(username)
|
|
158
|
-
const date2 = new Date().toUTCString()
|
|
159
|
-
const signature2 = await nockSignature({
|
|
160
|
-
url: `${origin}/user/ok/outbox`,
|
|
161
|
-
date: date2,
|
|
162
|
-
username
|
|
163
|
-
})
|
|
164
|
-
const headers2 = {
|
|
165
|
-
date: date2,
|
|
166
|
-
signature: signature2,
|
|
167
|
-
host: URL.parse(origin).host
|
|
168
|
-
}
|
|
169
|
-
const req2 = {
|
|
170
|
-
headers: headers2,
|
|
171
|
-
originalUrl,
|
|
172
|
-
method,
|
|
173
|
-
get: function (name) {
|
|
174
|
-
return this.headers[name.toLowerCase()]
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
await authenticator.authenticate(req2, res, next)
|
|
178
|
-
})
|
|
179
|
-
|
|
180
|
-
it('can authenticate a valid POST request', async () => {
|
|
181
|
-
const username = 'test3'
|
|
182
|
-
const type = 'Activity'
|
|
183
|
-
const activity = await as2.import({
|
|
184
|
-
id: nockFormat({ username, type }),
|
|
185
|
-
type
|
|
186
|
-
})
|
|
187
|
-
const rawBodyText = await activity.write()
|
|
188
|
-
const digest = await digester.digest(rawBodyText)
|
|
189
|
-
const date = new Date().toUTCString()
|
|
190
|
-
const method = 'POST'
|
|
191
|
-
const originalUrl = '/user/ok/inbox'
|
|
192
|
-
const signature = await nockSignature({
|
|
193
|
-
username,
|
|
194
|
-
url: `${origin}${originalUrl}`,
|
|
195
|
-
date,
|
|
196
|
-
digest,
|
|
197
|
-
method
|
|
198
|
-
})
|
|
199
|
-
const headers = {
|
|
200
|
-
date,
|
|
201
|
-
signature,
|
|
202
|
-
host: URL.parse(origin).host,
|
|
203
|
-
digest
|
|
204
|
-
}
|
|
205
|
-
const res = {
|
|
206
|
-
|
|
207
|
-
}
|
|
208
|
-
const req = {
|
|
209
|
-
headers,
|
|
210
|
-
originalUrl,
|
|
211
|
-
method,
|
|
212
|
-
get: function (name) {
|
|
213
|
-
return this.headers[name.toLowerCase()]
|
|
214
|
-
},
|
|
215
|
-
rawBodyText
|
|
216
|
-
}
|
|
217
|
-
await authenticator.authenticate(req, res, next)
|
|
218
|
-
})
|
|
219
|
-
|
|
220
|
-
it('skips a request that is not signed', async () => {
|
|
221
|
-
const date = new Date().toUTCString()
|
|
222
|
-
const method = 'GET'
|
|
223
|
-
const originalUrl = '/user/ok/outbox'
|
|
224
|
-
const headers = {
|
|
225
|
-
date,
|
|
226
|
-
host: URL.parse(origin).host
|
|
227
|
-
}
|
|
228
|
-
const res = {
|
|
229
|
-
|
|
230
|
-
}
|
|
231
|
-
const req = {
|
|
232
|
-
headers,
|
|
233
|
-
originalUrl,
|
|
234
|
-
method,
|
|
235
|
-
get: function (name) {
|
|
236
|
-
return this.headers[name.toLowerCase()]
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
await authenticator.authenticate(req, res, next)
|
|
240
|
-
})
|
|
241
|
-
|
|
242
|
-
it('can refuse a request signed with the wrong key', async () => {
|
|
243
|
-
const username = 'test'
|
|
244
|
-
const date = new Date().toUTCString()
|
|
245
|
-
const signature = await nockSignature({
|
|
246
|
-
url: `${origin}/user/ok/outbox`,
|
|
247
|
-
date,
|
|
248
|
-
username
|
|
249
|
-
})
|
|
250
|
-
const headers = {
|
|
251
|
-
date,
|
|
252
|
-
signature,
|
|
253
|
-
host: URL.parse(origin).host
|
|
254
|
-
}
|
|
255
|
-
const method = 'GET'
|
|
256
|
-
const originalUrl = '/user/ok/outbox'
|
|
257
|
-
const res = {
|
|
258
|
-
|
|
259
|
-
}
|
|
260
|
-
const req = {
|
|
261
|
-
headers,
|
|
262
|
-
originalUrl,
|
|
263
|
-
method,
|
|
264
|
-
get: function (name) {
|
|
265
|
-
return this.headers[name.toLowerCase()]
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
await authenticator.authenticate(req, res, next)
|
|
269
|
-
})
|
|
270
|
-
|
|
271
|
-
it('can refuse a request with a bad digest', async () => {
|
|
272
|
-
const username = 'test3'
|
|
273
|
-
const type = 'Activity'
|
|
274
|
-
const activity = await as2.import({
|
|
275
|
-
id: nockFormat({ username, type }),
|
|
276
|
-
type
|
|
277
|
-
})
|
|
278
|
-
const rawBodyText = await activity.write()
|
|
279
|
-
const digest = await digester.digest('This does not match the rawBodyText')
|
|
280
|
-
const date = new Date().toUTCString()
|
|
281
|
-
const method = 'POST'
|
|
282
|
-
const originalUrl = '/user/ok/inbox'
|
|
283
|
-
const signature = await nockSignature({
|
|
284
|
-
username,
|
|
285
|
-
url: `${origin}${originalUrl}`,
|
|
286
|
-
date,
|
|
287
|
-
digest,
|
|
288
|
-
method
|
|
289
|
-
})
|
|
290
|
-
const headers = {
|
|
291
|
-
date,
|
|
292
|
-
signature,
|
|
293
|
-
host: URL.parse(origin).host,
|
|
294
|
-
digest
|
|
295
|
-
}
|
|
296
|
-
const res = {
|
|
297
|
-
|
|
298
|
-
}
|
|
299
|
-
const req = {
|
|
300
|
-
headers,
|
|
301
|
-
originalUrl,
|
|
302
|
-
method,
|
|
303
|
-
get: function (name) {
|
|
304
|
-
return this.headers[name.toLowerCase()]
|
|
305
|
-
},
|
|
306
|
-
rawBodyText
|
|
307
|
-
}
|
|
308
|
-
await authenticator.authenticate(req, res, failNext)
|
|
309
|
-
})
|
|
310
|
-
|
|
311
|
-
it('can refuse a request with a missing digest', async () => {
|
|
312
|
-
const username = 'test3'
|
|
313
|
-
const type = 'Activity'
|
|
314
|
-
const activity = await as2.import({
|
|
315
|
-
id: nockFormat({ username, type }),
|
|
316
|
-
type
|
|
317
|
-
})
|
|
318
|
-
const rawBodyText = await activity.write()
|
|
319
|
-
const date = new Date().toUTCString()
|
|
320
|
-
const method = 'POST'
|
|
321
|
-
const originalUrl = '/user/ok/inbox'
|
|
322
|
-
const signature = await nockSignature({
|
|
323
|
-
username,
|
|
324
|
-
url: `${origin}${originalUrl}`,
|
|
325
|
-
date,
|
|
326
|
-
method
|
|
327
|
-
})
|
|
328
|
-
const headers = {
|
|
329
|
-
date,
|
|
330
|
-
signature,
|
|
331
|
-
host: URL.parse(origin).host
|
|
332
|
-
}
|
|
333
|
-
const res = {
|
|
334
|
-
|
|
335
|
-
}
|
|
336
|
-
const req = {
|
|
337
|
-
headers,
|
|
338
|
-
originalUrl,
|
|
339
|
-
method,
|
|
340
|
-
get: function (name) {
|
|
341
|
-
return this.headers[name.toLowerCase()]
|
|
342
|
-
},
|
|
343
|
-
rawBodyText
|
|
344
|
-
}
|
|
345
|
-
await authenticator.authenticate(req, res, failNext)
|
|
346
|
-
})
|
|
347
|
-
|
|
348
|
-
it('can refuse a request with a missing date', async () => {
|
|
349
|
-
const username = 'test'
|
|
350
|
-
const date = new Date().toUTCString()
|
|
351
|
-
const signature = await nockSignature({
|
|
352
|
-
url: `${origin}/user/ok/outbox`,
|
|
353
|
-
date,
|
|
354
|
-
username
|
|
355
|
-
})
|
|
356
|
-
const headers = {
|
|
357
|
-
signature,
|
|
358
|
-
host: URL.parse(origin).host
|
|
359
|
-
}
|
|
360
|
-
const method = 'GET'
|
|
361
|
-
const originalUrl = '/user/ok/outbox'
|
|
362
|
-
const res = {
|
|
363
|
-
|
|
364
|
-
}
|
|
365
|
-
const req = {
|
|
366
|
-
headers,
|
|
367
|
-
originalUrl,
|
|
368
|
-
method,
|
|
369
|
-
get: function (name) {
|
|
370
|
-
return this.headers[name.toLowerCase()]
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
await authenticator.authenticate(req, res, failNext)
|
|
374
|
-
})
|
|
375
|
-
|
|
376
|
-
it('can refuse a request with a badly formatted date', async () => {
|
|
377
|
-
const username = 'test'
|
|
378
|
-
const date = '3 Prairial CCXXXIII 14:00:35'
|
|
379
|
-
const signature = await nockSignature({
|
|
380
|
-
url: `${origin}/user/ok/outbox`,
|
|
381
|
-
date,
|
|
382
|
-
username
|
|
383
|
-
})
|
|
384
|
-
const headers = {
|
|
385
|
-
signature,
|
|
386
|
-
host: URL.parse(origin).host
|
|
387
|
-
}
|
|
388
|
-
const method = 'GET'
|
|
389
|
-
const originalUrl = '/user/ok/outbox'
|
|
390
|
-
const res = {
|
|
391
|
-
|
|
392
|
-
}
|
|
393
|
-
const req = {
|
|
394
|
-
headers,
|
|
395
|
-
originalUrl,
|
|
396
|
-
method,
|
|
397
|
-
get: function (name) {
|
|
398
|
-
return this.headers[name.toLowerCase()]
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
await authenticator.authenticate(req, res, failNext)
|
|
402
|
-
})
|
|
403
|
-
|
|
404
|
-
it('can refuse a request with a past date outside of the skew window', async () => {
|
|
405
|
-
const username = 'test'
|
|
406
|
-
// 10 days ago
|
|
407
|
-
const date = (new Date(Date.now() - 10 * 24 * 60 * 60 * 1000)).toUTCString()
|
|
408
|
-
logger.debug(date)
|
|
409
|
-
const signature = await nockSignature({
|
|
410
|
-
url: `${origin}/user/ok/outbox`,
|
|
411
|
-
date,
|
|
412
|
-
username
|
|
413
|
-
})
|
|
414
|
-
const headers = {
|
|
415
|
-
signature,
|
|
416
|
-
host: URL.parse(origin).host
|
|
417
|
-
}
|
|
418
|
-
const method = 'GET'
|
|
419
|
-
const originalUrl = '/user/ok/outbox'
|
|
420
|
-
const res = {
|
|
421
|
-
|
|
422
|
-
}
|
|
423
|
-
const req = {
|
|
424
|
-
headers,
|
|
425
|
-
originalUrl,
|
|
426
|
-
method,
|
|
427
|
-
get: function (name) {
|
|
428
|
-
return this.headers[name.toLowerCase()]
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
await authenticator.authenticate(req, res, failNext)
|
|
432
|
-
})
|
|
433
|
-
|
|
434
|
-
it('can refuse a request with a future date outside of the skew window', async () => {
|
|
435
|
-
const username = 'test'
|
|
436
|
-
// 10 days ago
|
|
437
|
-
const date = (new Date(Date.now() + 10 * 24 * 60 * 60 * 1000)).toUTCString()
|
|
438
|
-
logger.debug(date)
|
|
439
|
-
const signature = await nockSignature({
|
|
440
|
-
url: `${origin}/user/ok/outbox`,
|
|
441
|
-
date,
|
|
442
|
-
username
|
|
443
|
-
})
|
|
444
|
-
const headers = {
|
|
445
|
-
signature,
|
|
446
|
-
host: URL.parse(origin).host
|
|
447
|
-
}
|
|
448
|
-
const method = 'GET'
|
|
449
|
-
const originalUrl = '/user/ok/outbox'
|
|
450
|
-
const res = {
|
|
451
|
-
|
|
452
|
-
}
|
|
453
|
-
const req = {
|
|
454
|
-
headers,
|
|
455
|
-
originalUrl,
|
|
456
|
-
method,
|
|
457
|
-
get: function (name) {
|
|
458
|
-
return this.headers[name.toLowerCase()]
|
|
459
|
-
}
|
|
460
|
-
}
|
|
461
|
-
await authenticator.authenticate(req, res, failNext)
|
|
462
|
-
})
|
|
463
|
-
})
|