Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 2 additions & 1 deletion lib/api/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
module.exports = {
accounts: require('./accounts')
accounts: require('./accounts'),
messages: require('./messages')
}
105 changes: 105 additions & 0 deletions lib/api/messages/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
exports.send = send

const error = require('../../http-error')
const debug = require('debug')('solid:api:messages')
const utils = require('../../utils')
const sym = require('rdflib').sym
const url = require('url')
const waterfall = require('run-waterfall')

function send () {
return (req, res, next) => {
if (!req.session.userId) {
next(error(401, 'You need to be authenticated'))
return
}

if (!req.body.message || req.body.message.length < 0) {
next(error(406, 'You need to specify a message'))
return
}

if (!req.body.to) {
next(error(406, 'You need to specify a the destination'))
return
}

if (req.body.to.split(':').length !== 2) {
next(error(406, 'Destination badly formatted'))
return
}

waterfall([
(cb) => getLoggedUserName(req, cb),
(displayName, cb) => {
const vars = {
me: displayName,
message: req.body.message
}

if (req.body.to.split(':') === 'mailto' && req.app.locals.email) {
sendEmail(req, vars, cb)
} else {
cb(error(406, 'Messaging service not available'))
}
}
], (err) => {
if (err) {
next(err)
return
}

res.send('message sent')
})
}
}

function getLoggedUserName (req, callback) {
const ldp = req.app.locals.ldp
const baseUri = utils.uriAbs(req)
const webid = url.parse(req.session.userId)

ldp.graph(webid.hostname, '/' + webid.pathname, baseUri, function (err, graph) {
if (err) {
debug('cannot find graph of the user', req.session.userId || ldp.root, err)
// TODO for now only users of this IDP can send emails
callback(error(403, 'Your user cannot perform this operation'))
return
}

// TODO do a query
let displayName
graph
.statementsMatching(undefined, sym('http://xmlns.com/foaf/0.1/name'))
.some(function (statement) {
if (statement.object.value) {
displayName = statement.object.value
return true
}
})

if (!displayName) {
displayName = webid.hostname
}
callback(null, displayName)
})
}

function sendEmail (req, vars, callback) {
const emailService = req.app.locals.email
const emailData = {
from: 'no-reply@' + webid.hostname,
to: req.body.to.split(':')[1]
}
const webid = url.parse(req.session.userId)
var send = emailService.mailer.templateSender({
subject: 'Message from {{me}}',
text: 'You have received a message from {{me}}:\n{{message}}',
html: '<p>You have received a message from<strong>{{me}}</strong></p><p>{{message}}</p>'
}, {
from: emailData.from
})

// use template based sender to send a message
send({ to: emailData.to }, vars, callback)
}
4 changes: 3 additions & 1 deletion lib/create-app.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const AccountRecovery = require('./account-recovery')
const capabilityDiscovery = require('./capability-discovery')
const bodyParser = require('body-parser')
const API = require('./api')
var authentication = require('./handlers/authentication')

var corsSettings = cors({
methods: [
Expand Down Expand Up @@ -55,7 +56,7 @@ function createApp (argv = {}) {

// Setting options as local variable
app.locals.ldp = ldp
app.locals.appUrls = argv.apps // used for service capability discovery
app.locals.appUrls = argv.apps // used for service capability discovery

if (argv.email && argv.email.host) {
app.locals.email = new EmailService(argv.email)
Expand Down Expand Up @@ -136,6 +137,7 @@ function createApp (argv = {}) {
app.use('/api/accounts', needsOverwrite)
app.post('/api/accounts/signin', bodyParser.urlencoded({ extended: false }), API.accounts.signin())
app.post('/api/accounts/signout', API.accounts.signout())
app.post('/api/messages', authentication, bodyParser.urlencoded({ extended: false }), API.messages.send())
}

if (ldp.idp) {
Expand Down
43 changes: 22 additions & 21 deletions lib/ldp.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ function LDP (argv) {
LDP.prototype.stat = function (file, callback) {
fs.stat(file, function (err, stats) {
if (err) {
return callback(error(err, 'Can\'t read metadata'))
return callback(error(err, "Can't read metadata"))
}

return callback(null, stats)
Expand All @@ -105,7 +105,7 @@ LDP.prototype.readFile = function (filename, callback) {
{ 'encoding': 'utf8' },
function (err, data) {
if (err) {
return callback(error(err, 'Can\'t read file'))
return callback(error(err, "Can't read file"))
}

return callback(null, data)
Expand All @@ -121,15 +121,15 @@ LDP.prototype.readContainerMeta = function (directory, callback) {

ldp.readFile(directory + ldp.suffixMeta, function (err, data) {
if (err) {
return callback(error(err, 'Can\'t read meta file'))
return callback(error(err, "Can't read meta file"))
}

return callback(null, data)
})
}

LDP.prototype.listContainer = function (filename, uri, containerData,
contentType, callback) {
contentType, callback) {
var ldp = this
var host = url.parse(uri).hostname
var root = !ldp.idp ? ldp.root : ldp.root + host + '/'
Expand All @@ -141,7 +141,7 @@ LDP.prototype.listContainer = function (filename, uri, containerData,
$rdf.parse(containerData, resourceGraph, baseUri, 'text/turtle')
} catch (err) {
debug.handlers('GET -- Error parsing data: ' + err)
return callback(error(500, 'Can\'t parse container'))
return callback(error(500, "Can't parse container"))
}

async.waterfall([
Expand All @@ -163,24 +163,24 @@ LDP.prototype.listContainer = function (filename, uri, containerData,
next)
}
],
function (err, data) {
if (err) {
return callback(error(500, 'Can\'t list container'))
}
// TODO 'text/turtle' is fixed, should be contentType instead
// This forces one more translation turtle -> desired
serialize(resourceGraph, null, 'text/turtle', function (err, result) {
function (err, data) {
if (err) {
debug.handlers('GET -- Error serializing container: ' + err)
return callback(error(500, 'Can\'t serialize container'))
return callback(error(500, "Can't list container"))
}
return callback(null, result)
// TODO 'text/turtle' is fixed, should be contentType instead
// This forces one more translation turtle -> desired
serialize(resourceGraph, null, 'text/turtle', function (err, result) {
if (err) {
debug.handlers('GET -- Error serializing container: ' + err)
return callback(error(500, "Can't serialize container"))
}
return callback(null, result)
})
})
})
}

LDP.prototype.post = function (hostname, containerPath, slug, stream, container,
callback) {
callback) {
var ldp = this

debug.handlers('POST -- On parent: ' + containerPath)
Expand Down Expand Up @@ -258,6 +258,7 @@ LDP.prototype.graph = function (host, reqPath, baseUri, contentType, callback) {

var root = ldp.idp ? ldp.root + host + '/' : ldp.root
var filename = utils.uriToFilename(reqPath, root)
console.log(filename)

async.waterfall([
// Read file
Expand All @@ -274,15 +275,15 @@ LDP.prototype.graph = function (host, reqPath, baseUri, contentType, callback) {
}

LDP.prototype.get = function (host, reqPath, baseUri, includeBody, contentType,
callback) {
callback) {
var ldp = this
var root = !ldp.idp ? ldp.root : ldp.root + host + '/'
var filename = utils.uriToFilename(reqPath, root)

ldp.stat(filename, function (err, stats) {
// File does not exist
if (err) {
return callback(error(err, 'Can\'t find resource requested'))
return callback(error(err, "Can't find resource requested"))
}

// Just return, since resource exists
Expand Down Expand Up @@ -314,7 +315,7 @@ LDP.prototype.get = function (host, reqPath, baseUri, includeBody, contentType,
stream
.on('error', function (err) {
debug.handlers('GET -- Read error:' + err.message)
return callback(error(err, 'Can\'t create file ' + err))
return callback(error(err, "Can't create file " + err))
})
.on('open', function () {
debug.handlers('GET -- Read Start.')
Expand All @@ -334,7 +335,7 @@ LDP.prototype.delete = function (host, resourcePath, callback) {
var filename = utils.uriToFilename(resourcePath, root)
ldp.stat(filename, function (err, stats) {
if (err) {
return callback(error(404, 'Can\'t find ' + err))
return callback(error(404, "Can't find " + err))
}

if (stats.isDirectory()) {
Expand Down
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
},
"devDependencies": {
"chai": "^3.0.0",
"hippie": "^0.5.0",
"mocha": "^2.2.5",
"nock": "^7.0.2",
"rsvp": "^3.1.0",
Expand Down Expand Up @@ -97,5 +98,7 @@
"bin": {
"solid": "./bin/solid.js"
},
"engines" : { "node" : ">=6.0" }
"engines": {
"node": ">=6.0"
}
}
19 changes: 11 additions & 8 deletions test/api-accounts.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const expect = require('chai').expect
const nock = require('nock')
// In this test we always assume that we are Alice

describe('API', () => {
describe('Accounts API', () => {
let aliceServer
let bobServer
let alice
Expand Down Expand Up @@ -68,7 +68,7 @@ describe('API', () => {
if (bobServer) bobServer.close()
})

describe('APIs', () => {
describe('endpoints', () => {
describe('/api/accounts/signin', () => {
it('should complain if a URL is missing', (done) => {
alice.post('/api/accounts/signin')
Expand All @@ -81,18 +81,21 @@ describe('API', () => {
.expect(400)
.end(done)
})
it('should return a 400 if endpoint doesn\'t have Link Headers', (done) => {
it("should return a 400 if endpoint doesn't have Link Headers", (done) => {
nock('https://amazingwebsite.tld').intercept('/', 'OPTIONS').reply(200)
alice.post('/api/accounts/signin')
.send('webid=https://amazingwebsite.tld/')
.expect(400)
.end(done)
})
it('should return a 400 if endpoint doesn\'t have oidc in the headers', (done) => {
nock('https://amazingwebsite.tld').intercept('/', 'OPTIONS').reply(200, '', {
'Link': function (req, res, body) {
return '<https://oidc.amazingwebsite.tld>; rel="oidc.issuer"'
}})
it("should return a 400 if endpoint doesn't have oidc in the headers", (done) => {
nock('https://amazingwebsite.tld')
.intercept('/', 'OPTIONS')
.reply(200, '', {
'Link': function (req, res, body) {
return '<https://oidc.amazingwebsite.tld>; rel="oidc.issuer"'
}
})
alice.post('/api/accounts/signin')
.send('webid=https://amazingwebsite.tld/')
.expect(302)
Expand Down
Loading