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
12 changes: 6 additions & 6 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ node_modules/
*.swp
.tern-port
npm-debug.log
accounts
profile
inbox
.acl
config.json
settings
./accounts
./profile
./inbox
./.acl
./config.json
./settings
10 changes: 5 additions & 5 deletions .npmignore
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
config.json.bck
config.json
test
accounts
settings
profile
.acl
inbox
./accounts
./settings
./profile
./.acl
./inbox
8 changes: 4 additions & 4 deletions lib/account-recovery.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ function AccountRecovery (corsSettings, options = {}) {
text: 'Hello,\n' +
'You asked to retrieve your account: ' + account + '\n' +
'Copy this address in your browser addressbar:\n\n' +
'https://' + path.join(host, '/recovery/confirm?token=' + token) // TODO find a way to get the full url
'https://' + path.join(host, '/api/accounts/validateToken?token=' + token) // TODO find a way to get the full url
// html: ''
}
}
Expand All @@ -29,12 +29,12 @@ function AccountRecovery (corsSettings, options = {}) {
router.use(corsSettings)
}

router.get('/request', function (req, res, next) {
router.get('/recover', function (req, res, next) {
res.set('Content-Type', 'text/html')
res.sendFile(path.join(__dirname, '../static/account-recovery.html'))
})

router.post('/request', bodyParser.urlencoded({ extended: false }), function (req, res, next) {
router.post('/recover', bodyParser.urlencoded({ extended: false }), function (req, res, next) {
debug('getting request for account recovery', req.body.webid)
const ldp = req.app.locals.ldp
const emailService = req.app.locals.email
Expand Down Expand Up @@ -85,7 +85,7 @@ function AccountRecovery (corsSettings, options = {}) {
})
})

router.get('/confirm', function (req, res, next) {
router.get('/validateToken', function (req, res, next) {
if (!req.query.token) {
res.status(406).send('Token is required')
return
Expand Down
4 changes: 4 additions & 0 deletions lib/api/accounts/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module.exports = {
signin: require('./signin'),
signout: require('./signout')
}
33 changes: 33 additions & 0 deletions lib/api/accounts/signin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
module.exports = signin

const validUrl = require('valid-url')
const request = require('request')
const li = require('li')

function signin () {
return (req, res, next) => {
if (!validUrl.isUri(req.body.webid)) {
return res.status(400).send('This is not a valid URI')
}

request({ method: 'OPTIONS', uri: req.body.webid }, function (err, req) {
if (err) {
res.status(400).send('Did not find a valid endpoint')
return
}
if (!req.headers.link) {
res.status(400).send('The URI requested is not a valid endpoint')
return
}

const linkHeaders = li.parse(req.headers.link)
console.log(linkHeaders)
if (!linkHeaders['oidc.issuer']) {
res.status(400).send('The URI requested is not a valid endpoint')
return
}

res.redirect(linkHeaders['oidc.issuer'])
})
}
}
9 changes: 9 additions & 0 deletions lib/api/accounts/signout.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module.exports = signout

function signout () {
return (req, res, next) => {
req.session.userId = ''
req.session.identified = false
res.status(200).send()
}
}
3 changes: 3 additions & 0 deletions lib/api/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports = {
accounts: require('./accounts')
}
15 changes: 13 additions & 2 deletions lib/create-app.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ var path = require('path')
var EmailService = require('./email-service')
const AccountRecovery = require('./account-recovery')
const capabilityDiscovery = require('./capability-discovery')
const bodyParser = require('body-parser')
const API = require('./api')

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

if (ldp.webid) {
var accountRecovery = AccountRecovery(corsSettings, { redirect: '/' })
app.use('/recovery', accountRecovery)
// adds GET /api/accounts/recover
// adds POST /api/accounts/recover
// adds GET /api/accounts/validateToken
app.use('/api/accounts/', accountRecovery)
}

// Adding Multi-user support
Expand All @@ -113,8 +118,14 @@ function createApp (argv = {}) {
}
})
}
app.use('/accounts', needsOverwrite)

// adds POST /api/accounts/new
// adds POST /api/accounts/newCert
app.use('/api/accounts', needsOverwrite)
app.use('/', corsSettings, idp.get.bind(idp))

app.post('/api/accounts/signin', corsSettings, bodyParser.urlencoded({ extended: false }), API.accounts.signin())
app.post('/api/accounts/signout', corsSettings, API.accounts.signout())
}

if (ldp.idp) {
Expand Down
3 changes: 2 additions & 1 deletion lib/handlers/authentication.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@ function handler (req, res, next) {
return next()
})
} else if (ldp.auth === 'oidc') {
return next(error(500, 'OIDC not implemented yet'))
setEmptySession(req)
return next()
} else {
return next(error(500, 'Authentication method not supported'))
}
Expand Down
40 changes: 31 additions & 9 deletions lib/identity-provider.js
Original file line number Diff line number Diff line change
Expand Up @@ -515,16 +515,38 @@ IdentityProvider.prototype.post = function (req, res, next) {
debug('Create account with settings ', options)

waterfall([
function (callback) {
if (options.spkac && options.spkac.length > 0) {
spkac = new Buffer(stripLineEndings(options.spkac), 'utf-8')
webid('tls').generate({
spkac: spkac,
agent: agent // TODO generate agent
}, callback)
} else {
(callback) => {
if (this.auth !== 'oidc') {
return callback()
}

const oidc = req.app.locals.oidc

if (!oidc) {
debug('there is no OidcService')
return callback()
}

return oidc.client.users
.create({
email: options.email,
profile: agent,
name: options.name,
password: options.password
})
.then(() => callback())
.catch(callback)
},
(callback) => {
if (!(this.auth === 'tls' && options.spkac && options.spkac.length > 0)) {
return callback(null, false)
}

spkac = new Buffer(stripLineEndings(options.spkac), 'utf-8')
webid('tls').generate({
spkac: spkac,
agent: agent // TODO generate agent
}, callback)
},
function (newCert, callback) {
cert = newCert
Expand Down Expand Up @@ -587,7 +609,7 @@ IdentityProvider.prototype.middleware = function (corsSettings, firstUser) {
router.all('/*', function (req, res) {
var host = uriAbs(req)
// TODO replace the hardcoded link with an arg
res.redirect('https://solid.github.io/solid-signup/?acc=accounts/new&crt=accounts/cert&domain=' + host)
res.redirect('https://solid.github.io/solid-signup/?acc=api/accounts/new&crt=api/accounts/cert&domain=' + host)
})
router.use(errorHandler)

Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
"mocha": "^2.2.5",
"nock": "^7.0.2",
"rsvp": "^3.1.0",
"run-waterfall": "^1.1.3",
"sinon": "^1.17.4",
"standard": "^7.0.1",
"supertest": "^1.0.1"
Expand Down
128 changes: 128 additions & 0 deletions test/api-accounts.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
const Solid = require('../')
const parallel = require('run-parallel')
const waterfall = require('run-waterfall')
const path = require('path')
const supertest = require('supertest')
const expect = require('chai').expect
const nock = require('nock')
// In this test we always assume that we are Alice

describe('API', () => {
let aliceServer
let bobServer
let alice
let bob

const alicePod = Solid.createServer({
root: path.join(__dirname, '/resources/accounts-scenario/alice'),
sslKey: path.join(__dirname, '/keys/key.pem'),
sslCert: path.join(__dirname, '/keys/cert.pem'),
auth: 'oidc',
dataBrowser: false,
fileBrowser: false,
webid: true
})
const bobPod = Solid.createServer({
root: path.join(__dirname, '/resources/accounts-scenario/bob'),
sslKey: path.join(__dirname, '/keys/key.pem'),
sslCert: path.join(__dirname, '/keys/cert.pem'),
auth: 'oidc',
dataBrowser: false,
fileBrowser: false,
webid: true
})

function getBobFoo (alice, bob, done) {
bob.get('/foo')
.expect(401)
.end((err, res) => {
if (err) return done(err)
expect(res).to.match(/META http-equiv="refresh"/)
done()
})
}

function postBobDiscoverSignIn (alice, bob, done) {
done()
}

function entersPasswordAndConsent (alice, bob, done) {
done()
}

before(function (done) {
parallel([
(cb) => {
aliceServer = alicePod.listen(5000, cb)
alice = supertest('https://localhost:5000')
},
(cb) => {
bobServer = bobPod.listen(5001, cb)
bob = supertest('https://localhost:5001')
}
], done)
})

after(function () {
if (aliceServer) aliceServer.close()
if (bobServer) bobServer.close()
})

describe('APIs', () => {
describe('/api/accounts/signin', () => {
it('should complain if a URL is missing', (done) => {
alice.post('/api/accounts/signin')
.expect(400)
.end(done)
})
it('should complain if a URL is invalid', (done) => {
alice.post('/api/accounts/signin')
.send('webid=HELLO')
.expect(400)
.end(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"'
}})
alice.post('/api/accounts/signin')
.send('webid=https://amazingwebsite.tld/')
.expect(302)
.end((err, res) => {
expect(res.header.location).to.eql('https://oidc.amazingwebsite.tld')
done(err)
})
})
})
})

describe('Auth workflow', () => {
it.skip('step1: User tries to get /foo and gets 401 and meta redirect', (done) => {
getBobFoo(alice, bob, done)
})

it.skip('step2: User enters webId to signin', (done) => {
postBobDiscoverSignIn(alice, bob, done)
})

it.skip('step3: User enters password', (done) => {
entersPasswordAndConsent(alice, bob, done)
})

it.skip('entire flow', (done) => {
waterfall([
(cb) => getBobFoo(alice, bob, cb),
(cb) => postBobDiscoverSignIn(alice, bob, cb),
(cb) => entersPasswordAndConsent(alice, bob, cb)
], done)
})
})
})
Loading