diff --git a/package-lock.json b/package-lock.json index 1381c7b..0386cfc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -101,6 +101,11 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" }, + "bcryptjs": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz", + "integrity": "sha1-mrVie5PmBiH/fNrF2pczAn3x0Ms=" + }, "binary": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz", diff --git a/package.json b/package.json index 96b1328..f696d06 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,7 @@ "test": "echo \"Error: no test specified\" && exit 1" }, "dependencies": { + "bcryptjs": "^2.4.3", "express": "^4.16.4", "express-fileupload": "^1.1.3-alpha.1", "pug": "^2.0.3", diff --git a/server.js b/server.js index 9c2ecee..f3a257a 100644 --- a/server.js +++ b/server.js @@ -8,12 +8,14 @@ const { spawn } = require('child_process'); const pug = require('pug'); const unzip = require('unzip'); const touch = require('touch'); +const bcrypt = require('bcryptjs'); // Consts const port = 8000; -const pythonPath = '/home/pytron/miniconda3/envs/pytron/bin/python' +const pythonPath = '/usr/bin/python3' const aisPath = 'pytron_run/ai_manager/' const aisPathOld = 'pytron_run/ai_manager_old' +const hashPath = "__bcrypt__hash.txt" // Create the directories to store the files try { fs.mkdirSync(aisPath); } catch { } @@ -98,13 +100,41 @@ function parse(data) { } parsed.sortedAis.sort((a, b) => b.score - a.score); - parsed.sortedAis[0].rank = 1; - parsed.sortedAis[1].rank = 2; - parsed.sortedAis[2].rank = 3; + if (parsed.sortedAis.length > 0) parsed.sortedAis[0].rank = 1; + if (parsed.sortedAis.length > 1) parsed.sortedAis[1].rank = 2; + if (parsed.sortedAis.length > 2) parsed.sortedAis[2].rank = 3; return parsed; } +function saveArchiveAndRun(req, res) { + + let path = pathtools.join(aisPath, req.body.name); + try { fs.mkdirSync(path); } catch { } + + let zipfile = pathtools.join(path, 'archive.zip'); + req.files.archive.mv(zipfile, (err) => { + if (err != null) { + console.log(err); + res.render('error', {message: 'Unable to save the ZIP archive to the server'}); + return; + } + fs.createReadStream(zipfile) + .pipe(unzip.Extract({path})) + .on('close', () => { + // Touch __init__.py + touch(pathtools.join(path, '__init__.py'), () => { + // Trigger python_run + runPython(); + res.redirect('/'); + }); + }) + .on('error', () => { + res.render('error', {message: 'Failed to unzip the archive'}); + }); + }); +} + function startServer() { const app = express(); app.set('view engine', 'pug'); @@ -134,6 +164,10 @@ function startServer() { return; } + if (!req.body.password) { + res.render('error', {message: "You have to enter a password in the form"}); + } + if (req.body.name.indexOf('.') !== -1) { res.render('error', {message: "The name of your AI can't contain dots"}); } @@ -143,54 +177,89 @@ function startServer() { return; } - let path = pathtools.join(aisPath, req.body.name) - + let path = pathtools.join(aisPath, req.body.name); + let aiExisted; try { - if (fs.statSync(path).isDirectory()) { - // Move it to old - let version = 0; - for(;;) { - let movePath = pathtools.join(aisPathOld, req.body.name + '.' + version); - try { - fs.statSync(movePath); - // If the sync succeded, it means that the directory already exists - version++; - } catch { - // If we're here, it means that we found the right path. We - // will move the old one to the old directories, and save - // the new one - fs.renameSync(path, movePath); - break; - } - } - } - } catch { - // Nothing to do here + fs.statSync(path).isDirectory(); + aiExisted = true; + } catch (e) { + aiExisted = false; + fs.mkdirSync(path); } - fs.mkdirSync(path); - let zipfile = pathtools.join(path, 'archive.zip'); - req.files.archive.mv(zipfile, (err) => { - if (err != null) { - console.log(err); - res.render('error', {message: 'Unable to save the ZIP archive to the server'}); - return; - } - fs.createReadStream(zipfile) - .pipe(unzip.Extract({path})) - .on('close', () => { - // Touch __init__.py - touch(pathtools.join(path, '__init__.py'), () => { - // Trigger python_run - runPython(); - res.redirect('/'); - }); - }) - .on('error', () => { - res.render('error', {message: 'Failed to unzip the archive'}); + if (aiExisted) { + fs.readFile(pathtools.join(path, hashPath), 'utf-8', function(err, data) { + + if (err != null) { + res.render('error', {message: "Couldn't read hashed password"}); + console.log(err); + return; + } + + // If the AI already existed, verify the password + bcrypt.compare(req.body.password, data, function(err, success) { + if (err != null) { + res.render('error', {message: "Couldn't compare password"}); + return; + } + + if (!success) { + res.render('error', {message: "Authentication failed"}); + return; + } + + try { + if (fs.statSync(path).isDirectory()) { + // Move it to old + let version = 0; + for(;;) { + let movePath = pathtools.join(aisPathOld, req.body.name + '.' + version); + try { + fs.statSync(movePath); + // If the sync succeded, it means that the directory already exists + version++; + } catch { + // If we're here, it means that we found the right path. We + // will move the old one to the old directories, and save + // the new one + fs.renameSync(path, movePath); + break; + } + } + } + } catch { + // Nothing to do here + } + + // Save archive and run stuff + saveArchiveAndRun(req, res); }); - }); + }); + } else { + bcrypt.hash(req.body.password, 10, function(err, hash) { + + if (err != null) { + res.render('error', {message: "Couldn't hash password"}); + return; + } + + // Store hash in your password DB. + fs.writeFile(pathtools.join(path, hashPath), hash, (err) => { + + if (err != null) { + res.render('error', {message: "Couldn't save hashed password"}); + return; + } + + // Save archive and run stuff + saveArchiveAndRun(req, res); + + }); + + }); + } + }); app.use('/static', express.static('static')); diff --git a/views/upload.pug b/views/upload.pug index 642de5f..4eced28 100644 --- a/views/upload.pug +++ b/views/upload.pug @@ -16,11 +16,18 @@ block content p.content.has-text-justified | You can use an already existing name to | replace it with a new version of your AI. - p.content.has-text-justified - | You could also erase other people's AI, but - | please, don't do it, because I would be sad - | 😢 input.input(type="text", name="name", placeholder="Name of your AI") + .fielg + label.label Password + p.content.has-text-justified + | This password will allow you to update your + | AI. If the name you specify is not used, + | this password can be anything you want. If + | the name you specify already exists, you need + | to enter the password you entered when you + | uploaded the AI the first time. + input.input(type="password", name="password", placeholder="Password") + .field label.label Archive p.content.has-text-justified