diff --git a/lib/acl.js b/lib/acl.js index 30d6800c5..f316f4ff2 100644 --- a/lib/acl.js +++ b/lib/acl.js @@ -105,7 +105,8 @@ ACL.prototype.can = function (user, mode, resource, callback, options) { }) } -ACL.prototype.findAgentClass = function (graph, user, mode, resource, acl, callback) { +ACL.prototype.findAgentClass = function (graph, user, mode, resource, acl, + callback) { var self = this // Agent class statement @@ -131,7 +132,8 @@ ACL.prototype.findAgentClass = function (graph, user, mode, resource, acl, callb }, callback) } -ACL.prototype.findRule = function (graph, user, mode, resource, accessType, acl, callback, options) { +ACL.prototype.findRule = function (graph, user, mode, resource, accessType, acl, + callback, options) { var self = this // TODO check if this is necessary @@ -155,7 +157,8 @@ ACL.prototype.findRule = function (graph, user, mode, resource, accessType, acl, var statementSubject = statement.subject.uri // Check for origin - var matchOrigin = self.matchOrigin(graph, statementSubject, options.origin, options.host) + var matchOrigin = self.matchOrigin(graph, statementSubject, + options.origin, options.host) if (!matchOrigin) { debug('The request does not match the origin') return done(false) @@ -164,7 +167,8 @@ ACL.prototype.findRule = function (graph, user, mode, resource, accessType, acl, // Check for accessTo/defaultForNew if (!self.isAcl(resource) || accessType === 'defaultForNew') { debug('Checking for accessType:' + accessType) - var accesses = self.matchAccessType(graph, statementSubject, accessType, resource) + var accesses = self.matchAccessType(graph, statementSubject, accessType, + resource) if (!accesses) { debug('Cannot find accessType ' + accessType) return done(false) @@ -189,7 +193,8 @@ ACL.prototype.findRule = function (graph, user, mode, resource, accessType, acl, debug('Inspect agentClass') // Check for AgentClass - return self.findAgentClass(graph, user, mode, resource, statementSubject, done) + return self.findAgentClass(graph, user, mode, resource, statementSubject, + done) }, function (found) { if (!found) { @@ -209,7 +214,8 @@ ACL.prototype.getMode = function getMode (graph, mode) { 'http://www.w3.org/ns/auth/acl#' + mode) } -ACL.prototype.matchAccessType = function matchAccessType (graph, rule, accessType, uri) { +ACL.prototype.matchAccessType = function matchAccessType (graph, rule, + accessType, uri) { var self = this var matches = self.match( graph, @@ -250,7 +256,8 @@ function possibleACLs (uri, suffix) { var first = S(uri).endsWith(suffix) ? uri : uri + suffix var urls = [first] var parsedUri = url.parse(uri) - var baseUrl = (parsedUri.protocol ? parsedUri.protocol + '//' : '') + (parsedUri.host || '') + var baseUrl = (parsedUri.protocol ? parsedUri.protocol + '//' : '') + + (parsedUri.host || '') if (baseUrl + '/' === uri) { return urls } @@ -301,18 +308,19 @@ function getUserId (req, callback) { } var delegator = rdfVocab.debrack(onBehalfOf) - verifyDelegator(req.hostname, delegator, req.session.userId, function (err, res) { - if (err) { - err.status = 500 - return callback(err) - } + verifyDelegator(req.hostname, delegator, req.session.userId, + function (err, res) { + if (err) { + err.status = 500 + return callback(err) + } - if (res) { - debug('Request User ID (delegation) :' + delegator) - return callback(null, delegator) - } - return callback(null, req.session.userId) - }) + if (res) { + debug('Request User ID (delegation) :' + delegator) + return callback(null, delegator) + } + return callback(null, req.session.userId) + }) } function verifyDelegator (host, ldp, baseUri, delegator, delegatee, callback) { @@ -365,7 +373,9 @@ function allow (mode) { getUserId(req, function (err, userId) { if (err) return next(err) - var reqPath = res && res.locals && res.locals.path ? res.locals.path : req.path + var reqPath = res && res.locals && res.locals.path + ? res.locals.path + : req.path ldp.exists(req.hostname, reqPath, function (err, stat) { if (reqPath[reqPath.length - 1] !== '/' && !err && diff --git a/lib/identity-provider.js b/lib/identity-provider.js index a518efaf8..f1902d5ac 100644 --- a/lib/identity-provider.js +++ b/lib/identity-provider.js @@ -102,7 +102,8 @@ IdentityProvider.prototype.create = function (options, cert, callback) { graphs.push(self.putGraph(options.preferences, createPreferences(options))) } if (options.inbox) { - graphs.push(self.putGraph(options.inbox + self.suffixAcl, createInboxAcl(options))) + graphs.push(self.putGraph(options.inbox + self.suffixAcl, + createInboxAcl(options))) } if (options.defaultContainers && options.defaultContainers.length > 0) { throw new Error('default containers is not supported yet') @@ -382,10 +383,12 @@ IdentityProvider.prototype.get = function (req, res, next) { if (err && err.status === 404) { // TODO maybe a personalized page // or a redirect - debug('Account on ' + req.hostname + ' is available from ' + req.originalUrl) + debug('Account on ' + req.hostname + ' is available from ' + + req.originalUrl) return res.sendStatus(404) } else { - debug('Account on ' + req.hostname + ' is not available ' + req.originalUrl) + debug('Account on ' + req.hostname + ' is not available ' + + req.originalUrl) return next() } }) @@ -410,7 +413,8 @@ IdentityProvider.prototype.newCert = function (req, res, next) { debug('Requesting new cert for ' + options.webid) if (req.session.userId !== options.webid) { - debug('user is not logged in: ' + req.session.userId + ' is not ' + options.webid) + debug('user is not logged in: ' + req.session.userId + ' is not ' + + options.webid) var error = new Error('You are not logged in, so you can\'t create a certificate') error.status = 401 return next(error) @@ -423,7 +427,8 @@ IdentityProvider.prototype.newCert = function (req, res, next) { // Get a new cert webid('tls').generate({ spkac: spkac, - commonName: options.name + ' [on ' + options.host + ' created at ' + options.date.toDateString() + ']', + commonName: options.name + ' [on ' + options.host + ' created at ' + + options.date.toDateString() + ']', issuer: { commonName: options.host }, duration: 10, agent: options.webid @@ -465,32 +470,33 @@ IdentityProvider.prototype.getGraph = function (uri, callback) { var hostname = parsed.hostname var reqPath = parsed.path - self.store.get(hostname, reqPath, null, true, 'text/turtle', function (err, stream) { - if (err) { - debug('Cannot find WebID card') - var notexists = new Error('Cannot find WebID card') - notexists.status = 500 - return callback(notexists) - } - var data = '' - stream - .on('data', function (chunk) { - data += chunk - }) - .on('end', function () { - // TODO make sure that uri is correct - parse(data, uri, 'text/turtle', function (err, card) { - if (err) { - debug('WebId can\'t be parsed: ' + err.message) - var invalid = new Error('You have an invalid WebID card') - invalid.status = 500 - return callback(invalid) - } - - return callback(null, card) + self.store.get(hostname, reqPath, null, true, 'text/turtle', + function (err, stream) { + if (err) { + debug('Cannot find WebID card') + var notexists = new Error('Cannot find WebID card') + notexists.status = 500 + return callback(notexists) + } + var data = '' + stream + .on('data', function (chunk) { + data += chunk }) - }) - }) + .on('end', function () { + // TODO make sure that uri is correct + parse(data, uri, 'text/turtle', function (err, card) { + if (err) { + debug('WebId can\'t be parsed: ' + err.message) + var invalid = new Error('You have an invalid WebID card') + invalid.status = 500 + return callback(invalid) + } + + return callback(null, card) + }) + }) + }) } // Handle POST requests on account creation diff --git a/lib/ldp-container.js b/lib/ldp-container.js new file mode 100644 index 000000000..b293d960b --- /dev/null +++ b/lib/ldp-container.js @@ -0,0 +1,167 @@ +module.exports.addContainerStats = addContainerStats +module.exports.addFile = addFile +module.exports.addStats = addStats +module.exports.getMetadataGraph = getMetadataGraph +module.exports.readdir = readdir + +var $rdf = require('rdflib') +var debug = require('./debug') +var error = require('./http-error') +var fs = require('fs') +var ns = require('./vocab/ns').ns +var S = require('string') +var turtleExtension = '.ttl' + +function addContainerStats (ldp, filename, resourceGraph, next) { + ldp.stat(filename, function (err, containerStats) { + if (!err) { + addStats(resourceGraph, '', containerStats) + resourceGraph.add( + resourceGraph.sym(''), + ns.rdf('type'), + ns.ldp('BasicContainer')) + + resourceGraph.add( + resourceGraph.sym(''), + ns.rdf('type'), + ns.ldp('Container')) + } + next() + }) +} + +function addFile (ldp, resourceGraph, baseUri, uri, container, file, callback) { + // Skip .meta and .acl + if (S(file).endsWith(ldp.suffixMeta) || S(file).endsWith(ldp.suffixAcl)) { + return callback(null) + } + + // Get file stats + ldp.stat(container + file, function (err, stats) { + if (err) { + // File does not exist, skip + return callback(null) + } + + var fileSubject = file + (stats.isDirectory() ? '/' : '') + // var fileBaseUri = utils.filenameToBaseUri(fileSubject, uri, root) + + // Add fileStats to resource Graph + addStats(resourceGraph, fileSubject, stats) + + // Add to `contains` list + resourceGraph.add( + resourceGraph.sym(''), + ns.ldp('contains'), + resourceGraph.sym(fileSubject)) + + // Set up a metaFile path + var metaFile = container + file + + (stats.isDirectory() ? '/' : '') + + (S(file).endsWith(turtleExtension) ? '' : ldp.suffixMeta) + + getMetadataGraph(ldp, metaFile, baseUri, function (err, metadataGraph) { + if (err) { + metadataGraph = $rdf.graph() + } + + // Add Container or BasicContainer types + if (stats.isDirectory()) { + resourceGraph.add( + metadataGraph.sym(fileSubject), + ns.rdf('type'), + ns.ldp('BasicContainer')) + + resourceGraph.add( + metadataGraph.sym(fileSubject), + ns.rdf('type'), + ns.ldp('Container')) + } + // Add generic LDP type + resourceGraph.add( + metadataGraph.sym(fileSubject), + ns.rdf('type'), + ns.ldp('Resource')) + + // Add type from metadataGraph + metadataGraph + .statementsMatching( + metadataGraph.sym(baseUri), + ns.rdf('type'), + undefined) + .forEach(function (typeStatement) { + // If the current is a file and its type is BasicContainer, + // This is not possible, so do not infer its type! + if ( + ( + typeStatement.object.uri !== ns.ldp('BasicContainer').uri && + typeStatement.object.uri !== ns.ldp('Container').uri + ) || + !stats.isFile() + ) { + resourceGraph.add( + resourceGraph.sym(fileSubject), + typeStatement.predicate, + typeStatement.object) + } + }) + + return callback(null) + }) + }) +} + +function addStats (resourceGraph, baseUri, stats) { + resourceGraph.add( + resourceGraph.sym(baseUri), + ns.stat('mtime'), + stats.mtime.getTime() / 1000) + + resourceGraph.add( + resourceGraph.sym(baseUri), + ns.stat('size'), + stats.size) +} + +function readdir (filename, callback) { + debug.handlers('GET -- Reading directory') + fs.readdir(filename, function (err, files) { + if (err) { + debug.handlers('GET -- Error reading files: ' + err) + return callback(error(err, 'Can\'t read container')) + } + + debug.handlers('Files in directory: ' + files) + return callback(null, files) + }) +} + +function getMetadataGraph (ldp, metaFile, fileBaseUri, callback) { + ldp.stat(metaFile, function (err, metaStats) { + if (err) { + return callback(err) + } + + if (metaStats && metaStats.isFile()) { + ldp.readFile(metaFile, function (err, rawMetadata) { + if (err) { + return callback(err) + } + + var metadataGraph = $rdf.graph() + try { + $rdf.parse( + rawMetadata, + metadataGraph, + fileBaseUri, + 'text/turtle') + } catch (dirErr) { + return callback(error(err, 'Can\'t parse container metadata')) + } + return callback(null, metadataGraph) + }) + } else { + return callback(null, $rdf.graph()) + } + }) +} diff --git a/lib/ldp.js b/lib/ldp.js index 785010452..e072c97f7 100644 --- a/lib/ldp.js +++ b/lib/ldp.js @@ -10,14 +10,13 @@ var mkdirp = require('fs-extra').mkdirp var uuid = require('node-uuid') var debug = require('./debug') var utils = require('./utils') -var ns = require('./vocab/ns').ns var error = require('./http-error') var stringToStream = require('./utils').stringToStream var serialize = require('./utils').serialize var extend = require('extend') var doWhilst = require('async').doWhilst var rimraf = require('rimraf') -var turtleExtension = '.ttl' +var ldpContainer = require('./ldp-container') function LDP (argv) { argv = argv || {} @@ -53,7 +52,8 @@ function LDP (argv) { } if (this.fileBrowser !== false) { - this.fileBrowser = argv.fileBrowser || 'https://linkeddata.github.io/warp/#/list/' + this.fileBrowser = argv.fileBrowser || + 'https://linkeddata.github.io/warp/#/list/' } if (this.dataBrowser !== false) { @@ -124,147 +124,12 @@ LDP.prototype.readContainerMeta = function (directory, callback) { }) } -LDP.prototype.listContainer = function (filename, uri, containerData, contentType, callback) { +LDP.prototype.listContainer = function (filename, uri, containerData, + contentType, callback) { var ldp = this var host = url.parse(uri).hostname var root = !ldp.idp ? ldp.root : ldp.root + host + '/' - function addStats (resourceGraph, baseUri, stats) { - resourceGraph.add( - resourceGraph.sym(baseUri), - ns.stat('mtime'), - stats.mtime.getTime() / 1000) - - resourceGraph.add( - resourceGraph.sym(baseUri), - ns.stat('size'), - stats.size) - } - - function readdir (filename, callback) { - debug.handlers('GET -- Reading directory') - fs.readdir(filename, function (err, files) { - if (err) { - debug.handlers('GET -- Error reading files: ' + err) - return callback(error(err, 'Can\'t read container')) - } - - debug.handlers('Files in directory: ' + files) - return callback(null, files) - }) - } - - function getMetadataGraph (metaFile, fileBaseUri, callback) { - ldp.stat(metaFile, function (err, metaStats) { - if (err) { - return callback(err) - } - - if (metaStats && metaStats.isFile()) { - ldp.readFile(metaFile, function (err, rawMetadata) { - if (err) { - return callback(err) - } - - var metadataGraph = $rdf.graph() - try { - $rdf.parse( - rawMetadata, - metadataGraph, - fileBaseUri, - 'text/turtle') - } catch (dirErr) { - return callback(error(err, 'Can\'t parse container metadata')) - } - return callback(null, metadataGraph) - }) - } else { - return callback(null, $rdf.graph()) - } - }) - } - - function addFile (ldp, resourceGraph, baseUri, uri, container, file, callback) { - // Skip .meta and .acl - if (S(file).endsWith(ldp.suffixMeta) || S(file).endsWith(ldp.suffixAcl)) { - return callback(null) - } - - // Get file stats - ldp.stat(container + file, function (err, stats) { - if (err) { - // File does not exist, skip - return callback(null) - } - - var fileSubject = file + (stats.isDirectory() ? '/' : '') - // var fileBaseUri = utils.filenameToBaseUri(fileSubject, uri, root) - - // Add fileStats to resource Graph - addStats(resourceGraph, fileSubject, stats) - - // Add to `contains` list - resourceGraph.add( - resourceGraph.sym(''), - ns.ldp('contains'), - resourceGraph.sym(fileSubject)) - - // Set up a metaFile path - var metaFile = container + file + - (stats.isDirectory() ? '/' : '') + - (S(file).endsWith(turtleExtension) ? '' : ldp.suffixMeta) - - getMetadataGraph(metaFile, baseUri, function (err, metadataGraph) { - if (err) { - metadataGraph = $rdf.graph() - } - - // Add Container or BasicContainer types - if (stats.isDirectory()) { - resourceGraph.add( - metadataGraph.sym(fileSubject), - ns.rdf('type'), - ns.ldp('BasicContainer')) - - resourceGraph.add( - metadataGraph.sym(fileSubject), - ns.rdf('type'), - ns.ldp('Container')) - } - // Add generic LDP type - resourceGraph.add( - metadataGraph.sym(fileSubject), - ns.rdf('type'), - ns.ldp('Resource')) - - // Add type from metadataGraph - metadataGraph - .statementsMatching( - metadataGraph.sym(baseUri), - ns.rdf('type'), - undefined) - .forEach(function (typeStatement) { - // If the current is a file and its type is BasicContainer, - // This is not possible, so do not infer its type! - if ( - ( - typeStatement.object.uri !== ns.ldp('BasicContainer').uri && - typeStatement.object.uri !== ns.ldp('Container').uri - ) || - !stats.isFile() - ) { - resourceGraph.add( - resourceGraph.sym(fileSubject), - typeStatement.predicate, - typeStatement.object) - } - }) - - return callback(null) - }) - }) - } - var baseUri = utils.filenameToBaseUri(filename, uri, root) var resourceGraph = $rdf.graph() @@ -278,32 +143,18 @@ LDP.prototype.listContainer = function (filename, uri, containerData, contentTyp async.waterfall([ // add container stats function (next) { - ldp.stat(filename, function (err, containerStats) { - if (!err) { - addStats(resourceGraph, '', containerStats) - resourceGraph.add( - resourceGraph.sym(''), - ns.rdf('type'), - ns.ldp('BasicContainer')) - - resourceGraph.add( - resourceGraph.sym(''), - ns.rdf('type'), - ns.ldp('Container')) - } - next() - }) + ldpContainer.addContainerStats(ldp, filename, resourceGraph, next) }, // reading directory function (next) { - readdir(filename, next) + ldpContainer.readdir(filename, next) }, // Iterate through all the files function (files, next) { async.each( files, function (file, cb) { - addFile(ldp, resourceGraph, baseUri, uri, filename, file, cb) + ldpContainer.addFile(ldp, resourceGraph, baseUri, uri, filename, file, cb) }, next) } @@ -324,7 +175,8 @@ LDP.prototype.listContainer = function (filename, uri, containerData, contentTyp }) } -LDP.prototype.post = function (hostname, containerPath, slug, stream, container, callback) { +LDP.prototype.post = function (hostname, containerPath, slug, stream, container, + callback) { var ldp = this debug.handlers('POST -- On parent: ' + containerPath) @@ -361,13 +213,15 @@ LDP.prototype.put = function (host, resourcePath, stream, callback) { // PUT requests not supported on containers. Use POST instead if (filePath[filePath.length - 1] === '/') { - return callback(error(409, 'PUT not supported on containers, use POST instead')) + return callback(error(409, + 'PUT not supported on containers, use POST instead')) } mkdirp(path.dirname(filePath), function (err) { if (err) { debug.handlers('PUT -- Error creating directory: ' + err) - return callback(error(err, 'Failed to create the path to the new resource')) + return callback(error(err, + 'Failed to create the path to the new resource')) } var file = stream.pipe(fs.createWriteStream(filePath)) file.on('error', function () { @@ -384,7 +238,8 @@ LDP.prototype.exists = function (host, reqPath, callback) { this.get(host, reqPath, undefined, false, undefined, callback) } -LDP.prototype.get = function (host, reqPath, baseUri, includeBody, contentType, callback) { +LDP.prototype.get = function (host, reqPath, baseUri, includeBody, contentType, + callback) { var ldp = this var root = !ldp.idp ? ldp.root : ldp.root + host + '/' var filename = utils.uriToFilename(reqPath, root) @@ -407,16 +262,17 @@ LDP.prototype.get = function (host, reqPath, baseUri, includeBody, contentType, metaFile = '' } - ldp.listContainer(filename, baseUri, metaFile, contentType, function (err, data) { - if (err) { - debug.handlers('GET container -- Read error:' + err.message) - return callback(err) - } - var stream = stringToStream(data) - // TODO 'text/turtle' is fixed, should be contentType instead - // This forces one more translation turtle -> desired - return callback(null, stream, 'text/turtle', true) - }) + ldp.listContainer(filename, baseUri, metaFile, contentType, + function (err, data) { + if (err) { + debug.handlers('GET container -- Read error:' + err.message) + return callback(err) + } + var stream = stringToStream(data) + // TODO 'text/turtle' is fixed, should be contentType instead + // This forces one more translation turtle -> desired + return callback(null, stream, 'text/turtle', true) + }) }) } else { var stream = ldp.createReadStream(filename) diff --git a/lib/utils.js b/lib/utils.js index bbd0c6e48..b8e1b62c6 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -57,7 +57,9 @@ function uriBase (req) { function pathBasename (fullpath) { var bname = '' if (fullpath) { - bname = (fullpath.lastIndexOf('/') === fullpath.length - 1) ? '' : path.basename(fullpath) + bname = (fullpath.lastIndexOf('/') === fullpath.length - 1) + ? '' + : path.basename(fullpath) } return bname } @@ -99,7 +101,8 @@ function serialize (graph, baseUri, contentType, callback) { return callback(err) } if (result === undefined) { - return callback(new Error('Error serializing the graph to ' + contentType)) + return callback(new Error('Error serializing the graph to ' + + contentType)) } return callback(null, result)