Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/handlers/get.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const allow = require('./allow')
const translate = require('../utils.js').translate
const error = require('../http-error')

const RDFs = require('../ldp').RDF_MIME_TYPES
const RDFs = require('../ldp').mimeTypesAsArray()

async function handler (req, res, next) {
const ldp = req.app.locals.ldp
Expand Down
32 changes: 30 additions & 2 deletions lib/handlers/put.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,27 @@
module.exports = handler

const bodyParser = require('body-parser')
const debug = require('debug')('solid:put')
const getContentType = require('../utils').getContentType
const HTTPError = require('../http-error')
const { stringToStream } = require('../utils')
const LDP = require('../ldp')

async function handler (req, res, next) {
const ldp = req.app.locals.ldp
debug(req.originalUrl)
res.header('MS-Author-Via', 'SPARQL')

const contentType = req.get('content-type')
if (LDP.mimeTypeIsRdf(contentType) && isAclFile(req)) {
return bodyParser.text({ type: () => true })(req, res, () => putAcl(req, res, next))
}
return putStream(req, res, next)
}

async function putStream (req, res, next, stream = req) {
const ldp = req.app.locals.ldp
try {
await ldp.put(req, req, getContentType(req.headers))
await ldp.put(req, stream, getContentType(req.headers))
debug('succeded putting the file')

res.sendStatus(201)
Expand All @@ -20,3 +32,19 @@ async function handler (req, res, next) {
return next(err)
}
}

async function putAcl (req, res, next) {
const ldp = req.app.locals.ldp
const contentType = req.get('content-type')
const requestUri = `${req.protocol}//${req.get('host')}${req.originalUrl}`
if (ldp.isValidRdf(req.body, requestUri, contentType)) {
const stream = stringToStream(req.body)
return putStream(req, res, next, stream)
}
next(new HTTPError(400, 'RDF file contains invalid syntax'))
}

function isAclFile (req) {
const originalUrlParts = req.originalUrl.split('.')
return originalUrlParts[originalUrlParts.length - 1] === 'acl'
}
24 changes: 21 additions & 3 deletions lib/ldp.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const parse = require('./utils').parse
const fetch = require('node-fetch')
const { promisify } = require('util')

const RDF_MIME_TYPES = [
const RDF_MIME_TYPES = new Set([
'text/turtle', // .ttl
'text/n3', // .n3
'text/html', // RDFa
Expand All @@ -28,7 +28,7 @@ const RDF_MIME_TYPES = [
'application/rdf+xml', // .rdf
'application/ld+json', // .jsonld
'application/x-turtle'
]
])

class LDP {
constructor (argv = {}) {
Expand Down Expand Up @@ -203,6 +203,17 @@ class LDP {
return await this.put(path, stream, contentType)
}

isValidRdf (body, requestUri, contentType) {
const resourceGraph = $rdf.graph()
try {
$rdf.parse(body, resourceGraph, requestUri, contentType)
} catch (err) {
debug.ldp('VALIDATE -- Error parsing data: ' + err)
return false
}
return true
}

async put (url, stream, contentType) {
// PUT requests not supported on containers. Use POST instead
if ((url.url || url).endsWith('/')) {
Expand Down Expand Up @@ -451,6 +462,13 @@ class LDP {
}
return ensureNotExists(this, path.join(containerURI, filename))
}

static mimeTypeIsRdf (mimeType) {
return RDF_MIME_TYPES.has(mimeType)
}

static mimeTypesAsArray () {
return Array.from(RDF_MIME_TYPES)
}
}
module.exports = LDP
module.exports.RDF_MIME_TYPES = RDF_MIME_TYPES
12 changes: 6 additions & 6 deletions lib/models/account-template.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ const mime = require('mime-types')
const recursiveRead = require('recursive-readdir')
const fsUtils = require('../common/fs-utils')
const templateUtils = require('../common/template-utils')
const LDP = require('../ldp')

const RDF_MIME_TYPES = require('../ldp').RDF_MIME_TYPES
const TEMPLATE_EXTENSIONS = [ '.acl', '.meta', '.json', '.hbs', '.handlebars' ]
const TEMPLATE_FILES = [ 'card' ]

Expand All @@ -31,7 +31,6 @@ class AccountTemplate {
*/
constructor (options = {}) {
this.substitutions = options.substitutions || {}
this.rdfMimeTypes = options.rdfMimeTypes || RDF_MIME_TYPES
this.templateExtensions = options.templateExtensions || TEMPLATE_EXTENSIONS
this.templateFiles = options.templateFiles || TEMPLATE_FILES
}
Expand Down Expand Up @@ -136,11 +135,12 @@ class AccountTemplate {
* @return {boolean}
*/
isTemplate (filePath) {
let parsed = path.parse(filePath)
const parsed = path.parse(filePath)

let isRdf = this.rdfMimeTypes.includes(mime.lookup(filePath))
let isTemplateExtension = this.templateExtensions.includes(parsed.ext)
let isTemplateFile = this.templateFiles.includes(parsed.base) ||
const mimeType = mime.lookup(filePath)
const isRdf = LDP.mimeTypeIsRdf(mimeType)
const isTemplateExtension = this.templateExtensions.includes(parsed.ext)
const isTemplateFile = this.templateFiles.includes(parsed.base) ||
this.templateExtensions.includes(parsed.base) // the '/.acl' case

return isRdf || isTemplateExtension || isTemplateFile
Expand Down
8 changes: 2 additions & 6 deletions test/integration/cors-proxy-test.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,14 @@
var assert = require('chai').assert
var supertest = require('supertest')
var path = require('path')
var nock = require('nock')
var { checkDnsSettings } = require('../utils')

var ldnode = require('../../index')
var { checkDnsSettings, setupSupertestServer } = require('../utils')

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why a var instead of a const?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consistency.


describe('CORS Proxy', () => {
var ldp = ldnode({
var server = setupSupertestServer({
root: path.join(__dirname, '../resources'),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, if you want to go cross-platform, path.join(__dirname, '..', 'resources') would make Windows users happy.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, if you want to go cross-platform, path.join(__dirname, '..', 'resources') would make Windows users happy.

Is it necessary though? It seems like path.join will normalize it nevertheless.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, you made me look at the source code of path.join.

The call to win32.normalize does some … things. So it could be possible that it would normalise it on its own.

corsProxy: '/proxy',
webid: false
})
var server = supertest(ldp)

before(checkDnsSettings)

Expand Down
16 changes: 3 additions & 13 deletions test/integration/errors-test.js
Original file line number Diff line number Diff line change
@@ -1,30 +1,20 @@
var supertest = require('supertest')
var path = require('path')

// Helper functions for the FS
// var rm = require('./utils').rm
// var write = require('./utils').write
// var cp = require('./utils').cp
var read = require('./../utils').read

var ldnode = require('../../index')
const { read, setupSupertestServer } = require('./../utils')

describe('Error pages', function () {
// LDP with error pages
var errorLdp = ldnode({
const errorServer = setupSupertestServer({
root: path.join(__dirname, '../resources'),
errorPages: path.join(__dirname, '../resources/errorPages'),
webid: false
})
var errorServer = supertest(errorLdp)

// LDP with no error pages
var noErrorLdp = ldnode({
const noErrorServer = setupSupertestServer({
root: path.join(__dirname, '../resources'),
noErrorPages: true,
webid: false
})
var noErrorServer = supertest(noErrorLdp)

function defaultErrorPage (filepath, expected) {
var handler = function (res) {
Expand Down
6 changes: 2 additions & 4 deletions test/integration/formats-test.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
var supertest = require('supertest')
var ldnode = require('../../index')
var path = require('path')
const assert = require('chai').assert
const { setupSupertestServer } = require('../utils')

describe('formats', function () {
var ldp = ldnode.createServer({
const server = setupSupertestServer({
root: path.join(__dirname, '../resources'),
webid: false
})

var server = supertest(ldp)
describe('HTML', function () {
it('should return HTML containing "Hello, World!" if Accept is set to text/html', function (done) {
server.get('/hello.html')
Expand Down
6 changes: 2 additions & 4 deletions test/integration/http-test.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,19 @@
var supertest = require('supertest')
var fs = require('fs')
var li = require('li')
var ldnode = require('../../index')
var rm = require('./../utils').rm
var path = require('path')
const rdf = require('rdflib')
const { setupSupertestServer } = require('../utils')

var suffixAcl = '.acl'
var suffixMeta = '.meta'
var ldpServer = ldnode.createServer({
var server = setupSupertestServer({
live: true,
dataBrowserPath: 'default',
root: path.join(__dirname, '../resources'),
auth: 'oidc',
webid: false
})
var server = supertest(ldpServer)
var { assert, expect } = require('chai')

/**
Expand Down
51 changes: 51 additions & 0 deletions test/integration/validate-tts.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
const fs = require('fs')
const path = require('path')
const { setupSupertestServer } = require('../utils')

const server = setupSupertestServer({
live: true,
dataBrowserPath: 'default',
root: path.join(__dirname, '../resources'),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd have split it to path.join(__dirname, '..', 'resources'), but if it works everywhere …

auth: 'oidc',
webid: false
})

const invalidTurtleBody = fs.readFileSync(path.join(__dirname, '../resources/invalid1.ttl'), {
'encoding': 'utf8'
})

describe('HTTP requests with invalid Turtle syntax', () => {
describe('PUT API', () => {
it('is allowed with invalid TTL files in general', (done) => {
server.put('/invalid1.ttl')
.send(invalidTurtleBody)
.set('content-type', 'text/turtle')
.expect(201, done)
})

it('is not allowed with invalid ACL files', (done) => {
server.put('/invalid1.ttl.acl')
.send(invalidTurtleBody)
.set('content-type', 'text/turtle')
.expect(400, done)
})
})

describe('PATCH API', () => {
it('does not support patching of TTL files', (done) => {
server.patch('/patch-1-initial.ttl')
.send(invalidTurtleBody)
.set('content-type', 'text/turtle')
.expect(415, done)
})
})

describe('POST API (multipart)', () => {
it('does not validate files that are posted', (done) => {
server.post('/')
.attach('invalid1', path.join(__dirname, '../resources/invalid1.ttl'))
.attach('invalid2', path.join(__dirname, '../resources/invalid2.ttl'))
.expect(200, done)
})
})
})
13 changes: 13 additions & 0 deletions test/resources/invalid1.ttl
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
@prefix ldp: <http://www.w3.org/ns/ldp#>.
@prefix o: <http://example.org/ontology#>.

<http://example.org/netWorth/nw1/>
test o:NetWorth;
o:netWorthOf <http://example.org/users/JohnZSmith>;
o:asset
<assets/a1>,
<assets/a2>;
o:liability
<liabilities/l1>,
<liabilities/l2>,
<liabilities/l3>.
9 changes: 9 additions & 0 deletions test/resources/invalid2.ttl
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
@prefix txn: <http://example.org/data/transaction/> .
@prefix srv: <http://example.org/data/server/> .
@prefix log: <http://example.org/ont/transaction-log/> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .

txn:123 invalid log:Transaction ;
log:processedBy srv:A ;
log:processedAt "2015-10-16T10:22:23"^^xsd:dateTime ;
log:statusCode 200 .
19 changes: 16 additions & 3 deletions test/utils.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
var fs = require('fs-extra')
var rimraf = require('rimraf')
var path = require('path')
const fs = require('fs-extra')
const rimraf = require('rimraf')
const path = require('path')
const OIDCProvider = require('@solid/oidc-op')
const dns = require('dns')
const ldnode = require('../index')
const supertest = require('supertest')

const TEST_HOSTS = ['nic.localhost', 'tim.localhost', 'nicola.localhost']

Expand Down Expand Up @@ -80,3 +82,14 @@ exports.loadProvider = function loadProvider (configPath) {
return provider.initializeKeyChain(config.keys)
})
}

exports.createServer = createServer
function createServer (options) {
return ldnode.createServer(options)
}

exports.setupSupertestServer = setupSuperServer
function setupSuperServer (options) {
const ldpServer = createServer(options)
return supertest(ldpServer)
}