cors: Add proper full cors support to all the media routes
continuous-integration/appveyor/branch AppVeyor build succeeded Details

master v2.2.11
Jonatan Nilsson 2023-11-15 13:21:22 +00:00
parent bf522833a8
commit afde7fb89a
9 changed files with 437 additions and 105 deletions

View File

@ -16,6 +16,7 @@ export default class MediaRoutes {
}) })
this.siteCache = new Map() this.siteCache = new Map()
this.collator = new Intl.Collator('is-IS', { numeric: false, sensitivity: 'accent' }) this.collator = new Intl.Collator('is-IS', { numeric: false, sensitivity: 'accent' })
this.cors = CorsHandler({ allowedOrigins: ['*'] })
} }
register(server) { register(server) {
@ -30,7 +31,7 @@ export default class MediaRoutes {
server.flaska.post('/media/resize', [server.queryHandler()], this.resize.bind(this)) server.flaska.post('/media/resize', [server.queryHandler()], this.resize.bind(this))
server.flaska.post('/media/resize/:filename', [server.queryHandler(), server.jsonHandler()], this.resizeExisting.bind(this)) server.flaska.post('/media/resize/:filename', [server.queryHandler(), server.jsonHandler()], this.resizeExisting.bind(this))
server.flaska.delete('/media/:filename', [server.queryHandler()], this.remove.bind(this)) server.flaska.delete('/media/:filename', [server.queryHandler()], this.remove.bind(this))
server.flaska.options('/::path', [server.queryHandler(), this.security.verifyCorsEnabled], CorsHandler({ allowedOrigins: ['*'] })) server.flaska.options('/::path', [server.queryHandler(), this.security.verifyCorsEnabled], this.cors)
} }
init(server) { init(server) {
@ -90,20 +91,32 @@ export default class MediaRoutes {
} }
} }
getSite(ctx) {
let site = this.security.verifyToken(ctx)
if (this.security.hasCors(site)) {
this.cors(ctx)
}
return site
}
async listFiles(ctx) { async listFiles(ctx) {
let site = await this.security.verifyToken(ctx) let site = this.getSite(ctx)
ctx.body = this.filesCacheGet(site) ctx.body = this.filesCacheGet(site)
} }
async listPublicFiles(ctx) { async listPublicFiles(ctx) {
this.security.throwIfNotPublic(ctx.params.site) this.security.throwIfNotPublic(ctx.params.site)
if (this.security.hasCors(ctx.params.site)) {
this.cors(ctx)
}
ctx.body = this.filesCacheGet(ctx.params.site) ctx.body = this.filesCacheGet(ctx.params.site)
} }
async upload(ctx, noprefix = false) { async upload(ctx, noprefix = false) {
let site = await this.security.verifyToken(ctx) let site = this.getSite(ctx)
ctx.state.site = site ctx.state.site = site
let result = await this.formidable.uploadFile(ctx, ctx.state.site, noprefix) let result = await this.formidable.uploadFile(ctx, ctx.state.site, noprefix)
@ -182,7 +195,7 @@ export default class MediaRoutes {
} }
async resizeExisting(ctx) { async resizeExisting(ctx) {
let site = await this.security.verifyToken(ctx) let site = this.getSite(ctx)
ctx.state.site = site ctx.state.site = site
ctx.body = {} ctx.body = {}
@ -196,7 +209,7 @@ export default class MediaRoutes {
} }
async remove(ctx) { async remove(ctx) {
let site = await this.security.verifyToken(ctx) let site = this.getSite(ctx)
this.filesCacheRemove(site, ctx.params.filename) this.filesCacheRemove(site, ctx.params.filename)

View File

@ -25,6 +25,11 @@ export function verifyToken(ctx) {
} }
} }
export function hasCors(site) {
let sites = config.get('sites')
return sites[site]?.cors === true
}
export function verifyCorsEnabled(ctx) { export function verifyCorsEnabled(ctx) {
let site let site
try { try {
@ -32,9 +37,8 @@ export function verifyCorsEnabled(ctx) {
} catch (err) { } catch (err) {
throw new HttpError(404) throw new HttpError(404)
} }
let sites = config.get('sites') if (!hasCors(site)) {
if (!sites[site] || sites[site].cors !== true) {
throw new HttpError(404) throw new HttpError(404)
} }
} }

View File

@ -1,6 +1,6 @@
{ {
"name": "storage-upload", "name": "storage-upload",
"version": "2.2.10", "version": "2.2.11",
"description": "Micro service for uploading and image resizing files to a storage server.", "description": "Micro service for uploading and image resizing files to a storage server.",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

View File

@ -26,6 +26,9 @@ Client.prototype.customRequest = function(method = 'GET', path, body, options) {
host: urlObj.hostname, host: urlObj.hostname,
port: Number(urlObj.port), port: Number(urlObj.port),
path: urlObj.pathname + urlObj.search, path: urlObj.pathname + urlObj.search,
headers: {
origin: 'http://localhost'
}
})) }))
const req = http.request(opts) const req = http.request(opts)
@ -44,7 +47,9 @@ Client.prototype.customRequest = function(method = 'GET', path, body, options) {
}) })
res.on('end', function () { res.on('end', function () {
if (!output) return resolve(null) let headers = opts.includeHeaders ? res.headers : null
if (!output) return resolve(headers ? { headers } : null)
try { try {
output = JSON.parse(output) output = JSON.parse(output)
} catch (e) { } catch (e) {
@ -56,29 +61,29 @@ Client.prototype.customRequest = function(method = 'GET', path, body, options) {
err.status = output.status err.status = output.status
return reject(err) return reject(err)
} }
resolve(output) resolve(headers ? { headers, output } : output)
}) })
}) })
req.end() req.end()
}) })
} }
Client.prototype.get = function(url = '/') { Client.prototype.get = function(url = '/', options) {
return this.customRequest('GET', url, null) return this.customRequest('GET', url, null, options)
} }
Client.prototype.post = function(url = '/', body = {}) { Client.prototype.post = function(url = '/', body = {}, options) {
let parsed = JSON.stringify(body) let parsed = JSON.stringify(body)
return this.customRequest('POST', url, parsed, { return this.customRequest('POST', url, parsed, defaults(options, {
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
'Content-Length': parsed.length, 'Content-Length': parsed.length,
}, },
}) }))
} }
Client.prototype.del = function(url = '/', body = {}) { Client.prototype.del = function(url = '/', body = {}, options) {
return this.customRequest('DELETE', url, JSON.stringify(body)) return this.customRequest('DELETE', url, JSON.stringify(body), options)
} }
const random = (length = 8) => { const random = (length = 8) => {
@ -92,7 +97,7 @@ const random = (length = 8) => {
return str; return str;
} }
Client.prototype.upload = function(url, file, method = 'POST', body = {}) { Client.prototype.upload = function(url, file, method = 'POST', body = {}, options) {
return fs.readFile(file).then(data => { return fs.readFile(file).then(data => {
const crlf = '\r\n' const crlf = '\r\n'
const filename = path.basename(file) const filename = path.basename(file)
@ -114,12 +119,12 @@ Client.prototype.upload = function(url, file, method = 'POST', body = {}) {
Buffer.from(`${crlf}--${boundary}--`), Buffer.from(`${crlf}--${boundary}--`),
]) ])
return this.customRequest(method, url, multipartBody, { return this.customRequest(method, url, multipartBody, defaults(options, {
timeout: 5000, timeout: 5000,
headers: { headers: {
'Content-Type': 'multipart/form-data; boundary=' + boundary, 'Content-Type': 'multipart/form-data; boundary=' + boundary,
'Content-Length': multipartBody.length, 'Content-Length': multipartBody.length,
}, },
}) }))
}) })
} }

View File

@ -46,7 +46,14 @@ export function startServer() {
"keys": { "keys": {
"default@HS256": "asdf1234" "default@HS256": "asdf1234"
} }
} },
"existing_cors": {
"public": true,
"keys": {
"default@HS256": "asdf1234"
},
"cors": true
},
}, },
}) })

View File

@ -75,6 +75,7 @@ t.timeout(10000).describe('Media (API)', () => {
}) })
t.test('should work if specified has cors enabled', async () => { t.test('should work if specified has cors enabled', async () => {
const assertOrigin = 'http://localhost:9999'
let token = encode(null, { iss: 'development_cors' }, secret) let token = encode(null, { iss: 'development_cors' }, secret)
const testPaths = [ const testPaths = [
'/media', '/media',
@ -87,7 +88,12 @@ t.timeout(10000).describe('Media (API)', () => {
] ]
for (let path of testPaths) { for (let path of testPaths) {
await client.customRequest('OPTIONS', path + `?token=${token}`, null) let res = await client.customRequest('OPTIONS', path + `?token=${token}`, null, {
includeHeaders: true,
headers: { origin: assertOrigin, 'access-control-request-method': 'POST' },
})
assert.strictEqual(res.headers['access-control-allow-origin'], assertOrigin)
assert.match(res.headers['access-control-allow-methods'], /post/i)
} }
}) })
}) })
@ -146,27 +152,68 @@ t.timeout(10000).describe('Media (API)', () => {
let data = await assert.isFulfilled( let data = await assert.isFulfilled(
client.upload( client.upload(
`/media?token=${token}`, `/media?token=${token}`,
resolve('test.png') resolve('test.png'),
'POST',
{ },
{ includeHeaders: true }
) )
) )
testFiles.push(data.path) testFiles.push(data?.output?.path)
assert.ok(data) assert.ok(data.output)
assert.ok(data.filename) assert.ok(data.output.filename)
assert.ok(data.filename.startsWith(currYear)) assert.ok(data.output.filename.startsWith(currYear))
assert.ok(data.path) assert.ok(data.output.path)
let stats = await Promise.all([ let stats = await Promise.all([
fs.stat(resolve('test.png')), fs.stat(resolve('test.png')),
fs.stat(resolve(`../../public/${data.path}`)), fs.stat(resolve(`../../public/${data.output.path}`)),
]) ])
assert.strictEqual(stats[0].size, stats[1].size) assert.strictEqual(stats[0].size, stats[1].size)
let img = await sharp(resolve(`../../public/${data.path}`)).metadata() let img = await sharp(resolve(`../../public/${data.output.path}`)).metadata()
assert.strictEqual(img.width, 600) assert.strictEqual(img.width, 600)
assert.strictEqual(img.height, 700) assert.strictEqual(img.height, 700)
assert.strictEqual(img.format, 'png') assert.strictEqual(img.format, 'png')
assert.notOk(data.headers['access-control-allow-origin'])
})
t.test('should upload file and create file and return cors if site has cors', async () => {
const assertOrigin = 'http://localhost:9000'
let token = encode(null, { iss: 'development_cors' }, secret)
let data = await assert.isFulfilled(
client.upload(
`/media?token=${token}`,
resolve('test.png'),
'POST',
{ },
{
includeHeaders: true,
headers: { origin: assertOrigin, 'access-control-request-method': 'POST' },
}
)
)
testFiles.push(data.output?.path)
assert.ok(data.output)
assert.ok(data.output.filename)
assert.ok(data.output.filename.startsWith(currYear))
assert.ok(data.output.path)
let stats = await Promise.all([
fs.stat(resolve('test.png')),
fs.stat(resolve(`../../public/${data.output.path}`)),
])
assert.strictEqual(stats[0].size, stats[1].size)
let img = await sharp(resolve(`../../public/${data.output.path}`)).metadata()
assert.strictEqual(img.width, 600)
assert.strictEqual(img.height, 700)
assert.strictEqual(img.format, 'png')
assert.strictEqual(data.headers['access-control-allow-origin'], assertOrigin)
}) })
t.test('should properly replace spaces and other stuff', async () => { t.test('should properly replace spaces and other stuff', async () => {
@ -200,6 +247,35 @@ t.timeout(10000).describe('Media (API)', () => {
assert.strictEqual(img.height, 720) assert.strictEqual(img.height, 720)
assert.strictEqual(img.format, 'png') assert.strictEqual(img.format, 'png')
}) })
t.test('should return cors for cors-enabled sites', async () => {
let token = encode(null, { iss: 'development' }, secret)
let data = await assert.isFulfilled(
client.upload(
`/media?token=${token}`,
resolve('test.png')
)
)
testFiles.push(data.path)
assert.ok(data)
assert.ok(data.filename)
assert.ok(data.filename.startsWith(currYear))
assert.ok(data.path)
let stats = await Promise.all([
fs.stat(resolve('test.png')),
fs.stat(resolve(`../../public/${data.path}`)),
])
assert.strictEqual(stats[0].size, stats[1].size)
let img = await sharp(resolve(`../../public/${data.path}`)).metadata()
assert.strictEqual(img.width, 600)
assert.strictEqual(img.height, 700)
assert.strictEqual(img.format, 'png')
})
}) })
t.describe('POST /media/noprefix', function temp() { t.describe('POST /media/noprefix', function temp() {
@ -256,15 +332,18 @@ t.timeout(10000).describe('Media (API)', () => {
let data = await assert.isFulfilled( let data = await assert.isFulfilled(
client.upload( client.upload(
`/media/noprefix?token=${token}`, `/media/noprefix?token=${token}`,
resolve('test.png') resolve('test.png'),
'POST',
{ },
{ includeHeaders: true }
) )
) )
testFiles.push(data.path) testFiles.push(data?.output?.path)
assert.ok(data) assert.ok(data.output)
assert.strictEqual(data.filename, 'test.png') assert.strictEqual(data.output.filename, 'test.png')
assert.ok(data.path) assert.ok(data.output.path)
let stats = await Promise.all([ let stats = await Promise.all([
fs.stat(resolve('test.png')), fs.stat(resolve('test.png')),
@ -276,6 +355,44 @@ t.timeout(10000).describe('Media (API)', () => {
assert.strictEqual(img.width, 600) assert.strictEqual(img.width, 600)
assert.strictEqual(img.height, 700) assert.strictEqual(img.height, 700)
assert.strictEqual(img.format, 'png') assert.strictEqual(img.format, 'png')
assert.notOk(data.headers['access-control-allow-origin'])
})
t.test('should upload and create file with no prefix', async () => {
const assertOrigin = 'http://localhost:9000'
let token = encode(null, { iss: 'development_cors' }, secret)
let data = await assert.isFulfilled(
client.upload(
`/media/noprefix?token=${token}`,
resolve('test.png'),
'POST',
{ },
{
includeHeaders: true,
headers: { origin: assertOrigin, 'access-control-request-method': 'POST' },
}
)
)
testFiles.push(data?.output?.path)
assert.ok(data.output)
assert.strictEqual(data.output.filename, 'test.png')
assert.ok(data.output.path)
let stats = await Promise.all([
fs.stat(resolve('test.png')),
fs.stat(resolve('../../public/development/test.png')),
])
assert.strictEqual(stats[0].size, stats[1].size)
let img = await sharp(resolve('../../public/development/test.png')).metadata()
assert.strictEqual(img.width, 600)
assert.strictEqual(img.height, 700)
assert.strictEqual(img.format, 'png')
assert.strictEqual(data.headers['access-control-allow-origin'], assertOrigin)
}) })
t.test('should upload and create file with prefix if exists', async () => { t.test('should upload and create file with prefix if exists', async () => {
@ -374,27 +491,66 @@ t.timeout(10000).describe('Media (API)', () => {
`/media/resize?token=${token}`, `/media/resize?token=${token}`,
resolve('test.png'), resolve('test.png'),
'POST', 'POST',
{ } { },
{ includeHeaders: true }
) )
) )
testFiles.push(data.path) testFiles.push(data?.output?.path)
assert.ok(data) assert.ok(data.output)
assert.ok(data.filename) assert.ok(data.output.filename)
assert.ok(data.filename.startsWith(currYear)) assert.ok(data.output.filename.startsWith(currYear))
assert.ok(data.path) assert.ok(data.output.path)
let stats = await Promise.all([ let stats = await Promise.all([
fs.stat(resolve('test.png')), fs.stat(resolve('test.png')),
fs.stat(resolve(`../../public/${data.path}`)), fs.stat(resolve(`../../public/${data.output.path}`)),
]) ])
assert.strictEqual(stats[0].size, stats[1].size) assert.strictEqual(stats[0].size, stats[1].size)
let img = await sharp(resolve(`../../public/${data.path}`)).metadata() let img = await sharp(resolve(`../../public/${data.output.path}`)).metadata()
assert.strictEqual(img.width, 600) assert.strictEqual(img.width, 600)
assert.strictEqual(img.height, 700) assert.strictEqual(img.height, 700)
assert.strictEqual(img.format, 'png') assert.strictEqual(img.format, 'png')
assert.notOk(data.headers['access-control-allow-origin'])
})
t.test('should upload file and create file and return cors if site has cors', async () => {
const assertOrigin = 'http://localhost:9000'
let token = encode(null, { iss: 'development_cors' }, secret)
let data = await assert.isFulfilled(
client.upload(
`/media/resize?token=${token}`,
resolve('test.png'),
'POST',
{ },
{
includeHeaders: true,
headers: { origin: assertOrigin, 'access-control-request-method': 'POST' },
}
)
)
testFiles.push(data?.output?.path)
assert.ok(data.output)
assert.ok(data.output.filename)
assert.ok(data.output.filename.startsWith(currYear))
assert.ok(data.output.path)
let stats = await Promise.all([
fs.stat(resolve('test.png')),
fs.stat(resolve(`../../public/${data.output.path}`)),
])
assert.strictEqual(stats[0].size, stats[1].size)
let img = await sharp(resolve(`../../public/${data.output.path}`)).metadata()
assert.strictEqual(img.width, 600)
assert.strictEqual(img.height, 700)
assert.strictEqual(img.format, 'png')
assert.strictEqual(data.headers['access-control-allow-origin'], assertOrigin)
}) })
t.test('should upload file and create multiple sizes for file', async () => { t.test('should upload file and create multiple sizes for file', async () => {
@ -560,29 +716,40 @@ t.timeout(10000).describe('Media (API)', () => {
t.describe('POST /media/resize/:filename', function temp() { t.describe('POST /media/resize/:filename', function temp() {
let sourceFilename let sourceFilename
let sourceFilenameCors
let sourcePath let sourcePath
let sourcePathCors
t.before(async function() { t.before(async function() {
let token = encode(null, { iss: 'development' }, secret) let issuers = ['development', 'development_cors']
let data = await assert.isFulfilled( for (let iss of issuers) {
client.upload( let token = encode(null, { iss }, secret)
`/media/resize?token=${token}`,
resolve('test.png'), let data = await assert.isFulfilled(
'POST', client.upload(
{ } `/media/resize?token=${token}`,
resolve('test.png'),
'POST',
{ }
)
) )
)
testFiles.push(data.path) testFiles.push(data.path)
assert.ok(data) assert.ok(data)
assert.ok(data.filename) assert.ok(data.filename)
assert.ok(data.filename.startsWith(currYear)) assert.ok(data.filename.startsWith(currYear))
assert.ok(data.path) assert.ok(data.path)
sourceFilename = data.filename if (iss === 'development') {
sourcePath = data.path sourceFilename = data.filename
sourcePath = data.path
} else {
sourceFilenameCors = data.filename
sourcePathCors = data.path
}
}
}) })
t.test('should require authentication', async () => { t.test('should require authentication', async () => {
@ -658,34 +825,75 @@ t.timeout(10000).describe('Media (API)', () => {
compressionLevel: 9, compressionLevel: 9,
} }
}, },
} },
{ includeHeaders: true }
) )
) )
testFiles.push(data.test1.path) testFiles.push(data.output.test1.path)
testFiles.push(data.test2.path) testFiles.push(data.output.test2.path)
assert.ok(data.test1.filename) assert.ok(data.output.test1.filename)
assert.ok(data.test1.filename.startsWith(currYear)) assert.ok(data.output.test1.filename.startsWith(currYear))
assert.ok(data.test1.path) assert.ok(data.output.test1.path)
assert.ok(data.test2.filename) assert.ok(data.output.test2.filename)
assert.ok(data.test2.filename.startsWith(currYear)) assert.ok(data.output.test2.filename.startsWith(currYear))
assert.ok(data.test2.path) assert.ok(data.output.test2.path)
let img = await sharp(resolve(`../../public/${sourcePath}`)).metadata() let img = await sharp(resolve(`../../public/${sourcePath}`)).metadata()
assert.strictEqual(img.width, 600) assert.strictEqual(img.width, 600)
assert.strictEqual(img.height, 700) assert.strictEqual(img.height, 700)
assert.strictEqual(img.format, 'png') assert.strictEqual(img.format, 'png')
img = await sharp(resolve(`../../public/${data.test1.path}`)).metadata() img = await sharp(resolve(`../../public/${data.output.test1.path}`)).metadata()
assert.strictEqual(img.width, 320) assert.strictEqual(img.width, 320)
assert.strictEqual(img.height, 413) assert.strictEqual(img.height, 413)
assert.strictEqual(img.format, 'jpeg') assert.strictEqual(img.format, 'jpeg')
img = await sharp(resolve(`../../public/${data.test2.path}`)).metadata() img = await sharp(resolve(`../../public/${data.output.test2.path}`)).metadata()
assert.strictEqual(img.width, 150) assert.strictEqual(img.width, 150)
assert.strictEqual(img.height, 175) assert.strictEqual(img.height, 175)
assert.strictEqual(img.format, 'png') assert.strictEqual(img.format, 'png')
assert.notOk(data.headers['access-control-allow-origin'])
})
t.test('should create sizes for existing file and return cors if site has cors', async () => {
const assertOrigin = 'http://localhost:9000'
let token = encode(null, { iss: 'development_cors' }, secret)
let data = await assert.isFulfilled(
client.post(
`/media/resize/${sourceFilenameCors}?token=${token}`,
{
test2: {
format: 'png',
resize: { width: 150 },
png: { compressionLevel: 9 }
},
},
{
includeHeaders: true,
headers: { origin: assertOrigin, 'access-control-request-method': 'POST' },
}
)
)
testFiles.push(data.output.test2.path)
assert.ok(data.output.test2.filename)
assert.ok(data.output.test2.filename.startsWith(currYear))
assert.ok(data.output.test2.path)
let img = await sharp(resolve(`../../public/${sourcePath}`)).metadata()
assert.strictEqual(img.width, 600)
assert.strictEqual(img.height, 700)
assert.strictEqual(img.format, 'png')
img = await sharp(resolve(`../../public/${data.output.test2.path}`)).metadata()
assert.strictEqual(img.width, 150)
assert.strictEqual(img.height, 175)
assert.strictEqual(img.format, 'png')
assert.strictEqual(data.headers['access-control-allow-origin'], assertOrigin)
}) })
t.test('should base64 output of existing file', async () => { t.test('should base64 output of existing file', async () => {
@ -771,7 +979,7 @@ t.timeout(10000).describe('Media (API)', () => {
}) })
t.test('should remove the file', async () => { t.test('should remove the file', async () => {
let token = encode(null, { iss: 'existing' }, secret) let token = encode(null, { iss: 'development' }, secret)
let data = await client.upload( let data = await client.upload(
`/media/noprefix?token=${token}`, `/media/noprefix?token=${token}`,
@ -781,11 +989,11 @@ t.timeout(10000).describe('Media (API)', () => {
let filepath = data.path let filepath = data.path
testFiles.push(filepath) testFiles.push(filepath)
let files = await client.get('/media/existing') let files = await client.get('/media?token=' + token)
let found = false let found = false
for (let file of files) { for (let file of files) {
if (file.filename === 'test.png') { if (file.filename === data.filename) {
found = true found = true
} }
} }
@ -795,17 +1003,19 @@ t.timeout(10000).describe('Media (API)', () => {
fs.stat(resolve(`../../public/${filepath}`)) fs.stat(resolve(`../../public/${filepath}`))
) )
await assert.isFulfilled( let res = await assert.isFulfilled(
client.del(`/media/test.png?token=${token}`) client.del(`/media/${data.filename}?token=${token}`, {}, {
includeHeaders: true
})
) )
testFiles.splice(testFiles.length - 1) testFiles.splice(testFiles.length - 1)
files = await client.get('/media/existing') files = await client.get('/media?token=' + token)
found = false found = false
for (let file of files) { for (let file of files) {
if (file.filename === 'test.png') { if (file.filename === data.filename) {
found = true found = true
} }
} }
@ -814,6 +1024,58 @@ t.timeout(10000).describe('Media (API)', () => {
await assert.isRejected( await assert.isRejected(
fs.stat(resolve(`../../public/${filepath}`)) fs.stat(resolve(`../../public/${filepath}`))
) )
assert.notOk(res.headers['access-control-allow-origin'])
})
t.test('should remove the file and return cors if site has cors', async () => {
const assertOrigin = 'http://localhost:9000'
let token = encode(null, { iss: 'development_cors' }, secret)
let data = await client.upload(
`/media/noprefix?token=${token}`,
resolve('test.png')
)
let filepath = data.path
testFiles.push(filepath)
let files = await client.get('/media?token=' + token)
let found = false
for (let file of files) {
if (file.filename === data.filename) {
found = true
}
}
assert.ok(found)
await assert.isFulfilled(
fs.stat(resolve(`../../public/${filepath}`))
)
let res = await assert.isFulfilled(
client.del(`/media/${data.filename}?token=${token}`, {}, {
includeHeaders: true,
headers: { origin: assertOrigin, 'access-control-request-method': 'POST' },
})
)
testFiles.splice(testFiles.length - 1)
files = await client.get('/media?token=' + token)
found = false
for (let file of files) {
if (file.filename === data.filename) {
found = true
}
}
assert.notOk(found)
await assert.isRejected(
fs.stat(resolve(`../../public/${filepath}`))
)
assert.strictEqual(res.headers['access-control-allow-origin'], assertOrigin)
}) })
}) })
@ -860,17 +1122,39 @@ t.timeout(10000).describe('Media (API)', () => {
t.test('should return list of files in specified folder', async () => { t.test('should return list of files in specified folder', async () => {
let token = encode(null, { iss: 'development' }, secret) let token = encode(null, { iss: 'development' }, secret)
let data = await client.get('/media?token=' + token) let data = await client.get('/media?token=' + token, { includeHeaders: true })
assert.ok(data.length) assert.ok(data.output.length)
let found = false let found = false
for (let file of data) { for (let file of data.output) {
if (file.filename === '.gitkeep') { if (file.filename === '.gitkeep') {
found = true found = true
break break
} }
} }
assert.ok(found) assert.ok(found)
assert.notOk(data.headers['access-control-allow-origin'])
})
t.test('should return list of files in specified folder and return cors if site has cors', async () => {
const assertOrigin = 'http://localhost:9000'
let token = encode(null, { iss: 'development_cors' }, secret)
let data = await client.get('/media?token=' + token, {
includeHeaders: true,
headers: { origin: assertOrigin, 'access-control-request-method': 'POST' },
})
assert.ok(data.output.length)
let found = false
for (let file of data.output) {
if (file.filename === '.gitkeep') {
found = true
break
}
}
assert.ok(found)
assert.strictEqual(data.headers['access-control-allow-origin'], assertOrigin)
}) })
}) })
@ -890,11 +1174,25 @@ t.timeout(10000).describe('Media (API)', () => {
}) })
t.test('should otherwise return list of files for a public site', async () => { t.test('should otherwise return list of files for a public site', async () => {
let files = await client.get('/media/existing') let files = await client.get('/media/existing', { includeHeaders: true })
assert.strictEqual(files[0].filename, '20220105_101610_test1.jpg') assert.strictEqual(files.output[0].filename, '20220105_101610_test1.jpg')
assert.strictEqual(files[0].size, null) assert.strictEqual(files.output[0].size, null)
assert.strictEqual(files[1].filename, '20220105_101610_test2.png') assert.strictEqual(files.output[1].filename, '20220105_101610_test2.png')
assert.strictEqual(files[1].size, null) assert.strictEqual(files.output[1].size, null)
assert.notOk(files.headers['access-control-allow-origin'])
})
t.test('should otherwise return list of files for a public site and cors if site has cors', async () => {
const assertOrigin = 'http://localhost:9000'
let files = await client.get('/media/existing_cors', {
includeHeaders: true,
headers: { origin: assertOrigin, 'access-control-request-method': 'POST' },
})
assert.strictEqual(files.output[0].filename, '20220105_101610_test1.jpg')
assert.strictEqual(files.output[0].size, null)
assert.strictEqual(files.output[1].filename, '20220105_101610_test2.png')
assert.strictEqual(files.output[1].size, null)
assert.strictEqual(files.headers['access-control-allow-origin'], assertOrigin)
}) })
}) })
}) })

View File

@ -73,7 +73,7 @@ t.describe('#listFiles()', function() {
const stubGetCache = stub() const stubGetCache = stub()
const routes = new MediaRoutes({ const routes = new MediaRoutes({
security: { verifyToken: stubVerify }, security: { verifyToken: stubVerify, hasCors: stub() },
}) })
routes.filesCacheGet = stubGetCache routes.filesCacheGet = stubGetCache
@ -87,7 +87,7 @@ t.describe('#listFiles()', function() {
let ctx = createContext() let ctx = createContext()
const assertError = new Error('temp') const assertError = new Error('temp')
stubVerify.rejects(assertError) stubVerify.throws(assertError)
let err = await assert.isRejected(routes.listFiles(ctx)) let err = await assert.isRejected(routes.listFiles(ctx))
@ -102,7 +102,7 @@ t.describe('#listFiles()', function() {
let ctx = createContext() let ctx = createContext()
const assertSiteName = 'benshapiro' const assertSiteName = 'benshapiro'
const assertResult = { a: 1 } const assertResult = { a: 1 }
stubVerify.resolves(assertSiteName) stubVerify.returns(assertSiteName)
stubGetCache.returns(assertResult) stubGetCache.returns(assertResult)
await routes.listFiles(ctx) await routes.listFiles(ctx)
@ -118,7 +118,7 @@ t.describe('#listPublicFiles()', function() {
const stubGetCache = stub() const stubGetCache = stub()
const routes = new MediaRoutes({ const routes = new MediaRoutes({
security: { throwIfNotPublic: stubSitePublic }, security: { throwIfNotPublic: stubSitePublic, hasCors: stub() },
}) })
routes.filesCacheGet = stubGetCache routes.filesCacheGet = stubGetCache
@ -250,6 +250,7 @@ t.describe('#uploadNoPrefix', function() {
security: { security: {
verifyToken: stubVerify, verifyToken: stubVerify,
verifyBody: stub(), verifyBody: stub(),
hasCors: stub(),
}, },
formidable: { uploadFile: stubUpload, }, formidable: { uploadFile: stubUpload, },
fs: { stat: stubStat }, fs: { stat: stubStat },
@ -267,7 +268,7 @@ t.describe('#uploadNoPrefix', function() {
let ctx = createContext() let ctx = createContext()
const assertSiteName = 'benshapiro' const assertSiteName = 'benshapiro'
const assertError = new Error('hello') const assertError = new Error('hello')
stubVerify.resolves(assertSiteName) stubVerify.returns(assertSiteName)
stubUpload.rejects(assertError) stubUpload.rejects(assertError)
let err = await assert.isRejected(routes.uploadNoPrefix(ctx)) let err = await assert.isRejected(routes.uploadNoPrefix(ctx))
@ -300,6 +301,7 @@ t.describe('#resizeExisting', function() {
security: { security: {
verifyToken: stubVerifyToken, verifyToken: stubVerifyToken,
verifyBody: stubVerifyBody, verifyBody: stubVerifyBody,
hasCors: stub(),
}, },
fs: { stat: stubStat }, fs: { stat: stubStat },
sharp: stubSharp, sharp: stubSharp,
@ -338,7 +340,7 @@ t.describe('#resizeExisting', function() {
let ctx = createContext({ req: { body: { } } }) let ctx = createContext({ req: { body: { } } })
const assertError = new Error('temp') const assertError = new Error('temp')
stubVerifyToken.rejects(assertError) stubVerifyToken.throws(assertError)
let err = await assert.isRejected(routes.resizeExisting(ctx)) let err = await assert.isRejected(routes.resizeExisting(ctx))
@ -367,7 +369,7 @@ t.describe('#resizeExisting', function() {
const assertJpeg = { a: 1 } const assertJpeg = { a: 1 }
const assertFilename = 'asdfsafd' const assertFilename = 'asdfsafd'
const assertSite = 'mario' const assertSite = 'mario'
stubVerifyToken.resolves(assertSite) stubVerifyToken.returns(assertSite)
let ctx = createContext({ req: { body: { let ctx = createContext({ req: { body: {
[assertKey]: { [assertKey]: {
@ -418,7 +420,7 @@ t.describe('#resizeExisting', function() {
const assertPayload = { a: 1 } const assertPayload = { a: 1 }
const assertFilename = 'asdfsafd' const assertFilename = 'asdfsafd'
const assertSite = 'mario' const assertSite = 'mario'
stubVerifyToken.resolves(assertSite) stubVerifyToken.returns(assertSite)
let called = 0 let called = 0
stubStat.returnWith(function() { stubStat.returnWith(function() {
@ -467,7 +469,7 @@ t.describe('#resizeExisting', function() {
const assertValidKey2 = 'derp' const assertValidKey2 = 'derp'
const assertErrorKey = 'throwmyerr' const assertErrorKey = 'throwmyerr'
const assertErrorMessage = 'some message here' const assertErrorMessage = 'some message here'
stubVerifyToken.resolves('asdf') stubVerifyToken.returns('asdf')
let called = 0 let called = 0
stubStat.returnWith(function() { stubStat.returnWith(function() {
@ -512,7 +514,7 @@ t.describe('#resizeExisting', function() {
const assertFilename = 'asdfsafd.png' const assertFilename = 'asdfsafd.png'
const assertSite = 'mario' const assertSite = 'mario'
const assertBase64Data = 'asdf1234' const assertBase64Data = 'asdf1234'
stubVerifyToken.resolves(assertSite) stubVerifyToken.returns(assertSite)
let ctx = createContext({ req: { body: { let ctx = createContext({ req: { body: {
[assertKey]: { [assertKey]: {
@ -562,6 +564,7 @@ t.describe('#resize', function() {
security: { security: {
verifyToken: stubVerifyToken, verifyToken: stubVerifyToken,
verifyBody: stubVerifyBody, verifyBody: stubVerifyBody,
hasCors: stub(),
}, },
fs: { stat: stubStat }, fs: { stat: stubStat },
formidable: { uploadFile: stubUpload }, formidable: { uploadFile: stubUpload },
@ -618,7 +621,7 @@ t.describe('#resize', function() {
const assertJpeg = { a: 1 } const assertJpeg = { a: 1 }
const assertFilename = 'asdfsafd' const assertFilename = 'asdfsafd'
const assertSite = 'mario' const assertSite = 'mario'
stubVerifyToken.resolves(assertSite) stubVerifyToken.returns(assertSite)
stubUpload.resolves({ filename: assertFilename + '.png' }) stubUpload.resolves({ filename: assertFilename + '.png' })
let ctx = createContext({ req: { body: { let ctx = createContext({ req: { body: {
@ -669,7 +672,7 @@ t.describe('#resize', function() {
const assertPayload = { a: 1 } const assertPayload = { a: 1 }
const assertFilename = 'asdfsafd' const assertFilename = 'asdfsafd'
const assertSite = 'mario' const assertSite = 'mario'
stubVerifyToken.resolves(assertSite) stubVerifyToken.returns(assertSite)
stubUpload.resolves({ filename: assertFilename + '.png' }) stubUpload.resolves({ filename: assertFilename + '.png' })
let called = 0 let called = 0
@ -720,7 +723,7 @@ t.describe('#resize', function() {
const assertValidKey2 = 'derp' const assertValidKey2 = 'derp'
const assertErrorKey = 'throwmyerr' const assertErrorKey = 'throwmyerr'
const assertErrorMessage = 'some message here' const assertErrorMessage = 'some message here'
stubVerifyToken.resolves('asdf') stubVerifyToken.returns('asdf')
stubUpload.resolves({ filename: 'file.png' }) stubUpload.resolves({ filename: 'file.png' })
let called = 0 let called = 0
@ -765,7 +768,7 @@ t.describe('#resize', function() {
const assertFilename = 'asdfsafd.png' const assertFilename = 'asdfsafd.png'
const assertSite = 'mario' const assertSite = 'mario'
const assertBase64Data = 'asdf1234' const assertBase64Data = 'asdf1234'
stubVerifyToken.resolves(assertSite) stubVerifyToken.returns(assertSite)
stubUpload.resolves({ filename: assertFilename }) stubUpload.resolves({ filename: assertFilename })
let ctx = createContext({ req: { body: { let ctx = createContext({ req: { body: {
@ -811,6 +814,7 @@ basicUploadTestRoutes.forEach(function(name) {
security: { security: {
verifyToken: stubVerify, verifyToken: stubVerify,
verifyBody: stub(), verifyBody: stub(),
hasCors: stub(),
}, },
formidable: { uploadFile: stubUpload, }, formidable: { uploadFile: stubUpload, },
fs: { stat: stubStat }, fs: { stat: stubStat },
@ -827,7 +831,7 @@ basicUploadTestRoutes.forEach(function(name) {
let ctx = createContext({ req: { body: { } } }) let ctx = createContext({ req: { body: { } } })
const assertError = new Error('temp') const assertError = new Error('temp')
stubVerify.rejects(assertError) stubVerify.throws(assertError)
let err = await assert.isRejected(routes[name](ctx)) let err = await assert.isRejected(routes[name](ctx))
@ -842,7 +846,7 @@ basicUploadTestRoutes.forEach(function(name) {
let ctx = createContext({ req: { body: { } } }) let ctx = createContext({ req: { body: { } } })
const assertSiteName = 'benshapiro' const assertSiteName = 'benshapiro'
const assertError = new Error('hello') const assertError = new Error('hello')
stubVerify.resolves(assertSiteName) stubVerify.returns(assertSiteName)
stubUpload.rejects(assertError) stubUpload.rejects(assertError)
let err = await assert.isRejected(routes[name](ctx)) let err = await assert.isRejected(routes[name](ctx))
@ -860,7 +864,7 @@ basicUploadTestRoutes.forEach(function(name) {
const assertSize = 1241412 const assertSize = 1241412
const assertFilename = 'asdfsafd' const assertFilename = 'asdfsafd'
const assertSite = 'mario' const assertSite = 'mario'
stubVerify.resolves(assertSite) stubVerify.returns(assertSite)
stubUpload.resolves({ filename: assertFilename }) stubUpload.resolves({ filename: assertFilename })
stubStat.resolves({ size: assertSize }) stubStat.resolves({ size: assertSize })
await routes[name](ctx) await routes[name](ctx)
@ -887,6 +891,7 @@ t.describe('#remove()', function() {
const routes = new MediaRoutes({ const routes = new MediaRoutes({
security: { security: {
verifyToken: stubVerify, verifyToken: stubVerify,
hasCors: stub(),
}, },
fs: { unlink: stubUnlink }, fs: { unlink: stubUnlink },
}) })
@ -905,7 +910,7 @@ t.describe('#remove()', function() {
let ctx = createContext({ req: { body: { } } }) let ctx = createContext({ req: { body: { } } })
const assertError = new Error('temp') const assertError = new Error('temp')
stubVerify.rejects(assertError) stubVerify.throws(assertError)
let err = await assert.isRejected(routes.remove(ctx)) let err = await assert.isRejected(routes.remove(ctx))
@ -924,7 +929,7 @@ t.describe('#remove()', function() {
let ctx = createContext({ req: { body: { } } }) let ctx = createContext({ req: { body: { } } })
ctx.params.filename = assertFilename ctx.params.filename = assertFilename
stubVerify.resolves(assertSiteName) stubVerify.returns(assertSiteName)
stubUnlink.rejects(assertError) stubUnlink.rejects(assertError)
let err = await assert.isRejected(routes.remove(ctx)) let err = await assert.isRejected(routes.remove(ctx))
@ -948,7 +953,7 @@ t.describe('#remove()', function() {
let ctx = createContext({ req: { body: { } } }) let ctx = createContext({ req: { body: { } } })
ctx.params.filename = assertFilename ctx.params.filename = assertFilename
stubVerify.resolves(assertSiteName) stubVerify.returns(assertSiteName)
await routes.remove(ctx) await routes.remove(ctx)