diff --git a/.gitignore b/.gitignore index 05c85a695..3711ac935 100644 --- a/.gitignore +++ b/.gitignore @@ -3,8 +3,3 @@ node_modules/ *.swp .tern-port npm-debug.log -.acl -profile/ -accounts/ -settings/ -temp/ diff --git a/README.md b/README.md index ee5a9a7e0..c3ff27f20 100644 --- a/README.md +++ b/README.md @@ -37,13 +37,6 @@ $ ldnode --port 8443 --ssl-key path/to/ssl-key.pem --ssl-cert path/to/ssl-cert.p # Solid server (ldnode v0.2.24) running on https://localhost:8443/ ``` -First time user? If you have never run `ldnode` before, let's get you a WebID to access your server. -```bash -$ ldnode --port 8443 --ssl-key path/to/ssl-key.pem --ssl-cert path/to/ssl-cert.pem --create-admin -# Action required: Create your admin account on https://localhost:8080/ -# When done, stop your server (+c) and restart without "--create-admin" -``` - If you want to run `ldnode` on a particular folder (different from the one you are in, e.g. `path/to/folder`): ```bash $ ldnode --root path/to/folder --port 8443 --ssl-key path/to/ssl-key.pem --ssl-cert path/to/ssl-cert.pem @@ -104,7 +97,6 @@ Options: --ssl-cert Path to the SSL certificate key in PEM format --allow-signup Allow users to register their WebID on subdomains - --create-admin Allow a user to set up their initial identity in single-user mode --no-live Disable live support through WebSockets --default-app URI to use as a default app for resources (default: https://linkeddata.github.io/warp/#/list/) --proxy Use a proxy on example.tld/proxyPath diff --git a/bin/ldnode.js b/bin/ldnode.js index f97034ed6..778d89a0f 100644 --- a/bin/ldnode.js +++ b/bin/ldnode.js @@ -29,6 +29,9 @@ var argv = require('nomnom') full: 'webid', flag: true }) + .option('owner', { + help: 'Set the owner of the storage' + }) .option('key', { help: 'Path to the SSL private key in PEM format', full: 'ssl-key' @@ -42,11 +45,6 @@ var argv = require('nomnom') full: 'allow-signup', flag: true }) - .option('createAdmin', { - full: 'create-admin', - flag: true, - help: 'Allow a user to set up their initial identity in single-user mode' - }) .option('noLive', { full: 'no-live', help: 'Disable live support through WebSockets', @@ -134,6 +132,35 @@ function bin (argv) { }) } + if (argv.owner) { + var rootPath = argv.root + if (!rootPath) { + rootPath = process.cwd() + } + if (!(rootPath.endsWith('/'))) { + rootPath += '/' + } + rootPath += (argv.suffixAcl || '.acl') + + var defaultAcl = `@prefix n0: . + @prefix n2: . + + <#owner> + a n0:Authorization; + n0:accessTo <./>; + n0:agent <${argv.owner}>; + n0:defaultForNew <./>; + n0:mode n0:Control, n0:Read, n0:Write. + <#everyone> + a n0:Authorization; + n0: n2:Agent; + n0:accessTo <./>; + n0:defaultForNew <./>; + n0:mode n0:Read.' > .acl` + + fs.writeFileSync(rootPath, defaultAcl) + } + // Finally starting ldnode var ldnode = require('../') var app @@ -154,13 +181,8 @@ function bin (argv) { } app.listen(argv.port, function () { fs.readFile(path.resolve(__dirname, '../package.json'), 'utf-8', function (_, file) { - if (argv.createAdmin) { - console.log('Action required: Create your admin account on \u001b[4mhttps://localhost:' + argv.port + '/\u001b[0m') - console.log('When done, stop your server (+c) and restart without "--create-admin"') - } else { - console.log('Solid server (ldnode v' + JSON.parse(file).version + ') running on \u001b[4mhttps://localhost:' + argv.port + '/\u001b[0m') - console.log('Press +c to stop') - } + console.log('Solid server (ldnode v' + JSON.parse(file).version + ') running on \u001b[4mhttps://localhost:' + argv.port + '/\u001b[0m') + console.log('Press +c to stop') }) }) } diff --git a/lib/create-app.js b/lib/create-app.js index 5c7065c46..b7e2c7bf7 100644 --- a/lib/create-app.js +++ b/lib/create-app.js @@ -24,6 +24,21 @@ function createApp (argv) { var ldp = new LDP(argv) var app = express() + // check if we have master ACL or not + var masterAcl + var checkMasterAcl = function (req, callback) { + if (masterAcl) { + return callback(true) + } + + ldp.exists(req.hostname, '/' + ldp.suffixAcl, function (err) { + if (!err) { + masterAcl = true + } + callback(!err) + }) + } + // Setting options as local variable app.locals.ldp = ldp @@ -63,15 +78,26 @@ function createApp (argv) { } // Adding Multi-user support - if (ldp.idp || ldp.createAdmin) { + if (ldp.webid) { var idp = IdentityProvider({ store: ldp, suffixAcl: ldp.suffixAcl, - overwrite: ldp.createAdmin, settings: 'settings', inbox: 'inbox' }) - app.use('/accounts', idp.middleware(corsSettings)) + var needsOverwrite = function (req, res, next) { + checkMasterAcl(req, function (found) { + if (!found) { + // this allows IdentityProvider to overwrite root acls + idp.middleware(corsSettings, true)(req, res, next) + } else if (found && ldp.idp) { + idp.middleware(corsSettings)(req, res, next) + } else { + next() + } + }) + } + app.use('/accounts', needsOverwrite) app.use('/', corsSettings, idp.get.bind(idp)) } @@ -79,13 +105,22 @@ function createApp (argv) { app.use(vhost('*', LdpMiddleware(corsSettings))) } - if (ldp.createAdmin) { - app.get('/', function (req, res) { - res.set('Content-Type', 'text/html') - var signup = path.join(__dirname, '../static/signup.html') - res.sendFile(signup) + app.get('/', function (req, res, next) { + // Do not bother showing html page can't be read + if (!req.accepts('text/html') || !ldp.webid) { + return next() + } + + checkMasterAcl(req, function (found) { + if (!found) { + res.set('Content-Type', 'text/html') + var signup = path.join(__dirname, '../static/signup.html') + res.sendFile(signup) + } else { + next() + } }) - } + }) app.use('/', LdpMiddleware(corsSettings)) return app diff --git a/lib/identity-provider.js b/lib/identity-provider.js index f1902d5ac..aebaa34d0 100644 --- a/lib/identity-provider.js +++ b/lib/identity-provider.js @@ -39,7 +39,6 @@ function IdentityProvider (options) { this.buildURI = options.buildURI || defaultBuildURI this.suffixAcl = options.suffixAcl this.defaultContainers = options.defaultContainers || defaultContainers - this.overwrite = options.overwrite this.inbox = options.inbox this.settings = options.settings } @@ -113,7 +112,7 @@ IdentityProvider.prototype.create = function (options, cert, callback) { var subdomain = options.host.split(':')[0] self.store.exists(subdomain, '/', function (err) { // if page exists, cannot create account - if (!self.overwrite && (!err || err.status !== 404)) { + if (!options.firstUser && (!err || err.status !== 404)) { debug('Cannot create ' + subdomain + ', it already exists') var error = new Error('Account already exists') error.status = 406 @@ -511,6 +510,7 @@ IdentityProvider.prototype.post = function (req, res, next) { var self = this var options = req.body options.host = req.get('host') + options.firstUser = res.locals.firstUser var agent = self.agent(options) var spkac = null var cert = null @@ -558,14 +558,15 @@ IdentityProvider.prototype.post = function (req, res, next) { } // Middleware (or Router) to serve the IdentityProvider -IdentityProvider.prototype.middleware = function (corsSettings) { +IdentityProvider.prototype.middleware = function (corsSettings, firstUser) { var router = express.Router('/') var parser = bodyParser.urlencoded({ extended: false }) if (corsSettings) { router.use(corsSettings) } - router.post('/new', parser, this.post.bind(this)) + + router.post('/new', parser, setFirstUser(firstUser), this.post.bind(this)) router.post('/cert', parser, this.newCert.bind(this)) router.all('/*', function (req, res) { var host = uriAbs(req) @@ -576,3 +577,10 @@ IdentityProvider.prototype.middleware = function (corsSettings) { return router } + +function setFirstUser (isFirstUser) { + return function (req, res, next) { + res.locals.firstUser = isFirstUser + next() + } +} diff --git a/static/signup.html b/static/signup.html index 16d94aed9..99e319449 100644 --- a/static/signup.html +++ b/static/signup.html @@ -97,8 +97,7 @@

Finish by issuing credentials in the form of a certificate

document.getElementById('cert').style.display = 'none' var done = document.createElement('div') done.innerHTML = '

You\'re all set!

' - done.innerHTML += '

Please restart your server without the --create-admin parameter.

' - done.innerHTML += '

If an error occured and a certificate was not installed, please reload this page and start again.

' + done.innerHTML += '

as soon as you will reset the page, you will be logged in!

' document.querySelector('body').appendChild(done) } diff --git a/test/resources/acl/owner-only/.acl b/test/resources/acl/owner-only/.acl new file mode 100644 index 000000000..54343d2ce --- /dev/null +++ b/test/resources/acl/owner-only/.acl @@ -0,0 +1,4 @@ +<#0> + <./> ; + ; + , . \ No newline at end of file