Added tests
parent
2f3c094ca3
commit
0d49d5ce6c
|
@ -8,4 +8,5 @@ config.js
|
|||
.vscode
|
||||
.idea
|
||||
database.sqlite
|
||||
config.js
|
||||
config.js
|
||||
test/.devKeys
|
|
@ -8,13 +8,13 @@ async function validateJWT(token, key) {
|
|||
try {
|
||||
return jwt.verify(token.replace("JWT ", ""), key, {algorithms: ['RS256']});
|
||||
} catch (exception) {
|
||||
// TODO add logger to authentication controller
|
||||
//logger.error(exception);
|
||||
//logger.warn(exception)
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
async function getAuthenticatedAccount(req, res) {
|
||||
const sessionCookie = (req.signedCookies !== undefined ? req.signedCookies.session : null);
|
||||
if (!sessionCookie || sessionCookie.expires <= Date.now()) { return null; }
|
||||
|
|
|
@ -28,7 +28,7 @@ function mkDirByPathSync(targetDir, {isRelativeToScript = false} = {}) {
|
|||
try {
|
||||
fs.mkdirSync(curDir);
|
||||
} catch (err) {
|
||||
console.debug(err);
|
||||
//console.debug(err);
|
||||
if (err.code === 'EEXIST') { // curDir already exists!
|
||||
return curDir;
|
||||
}
|
||||
|
|
|
@ -4,13 +4,15 @@
|
|||
"description": "replacement for comma.ai backend and useradmin dashboard. can be combined with a modified cabana instance.",
|
||||
"main": "server.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"test": "mocha",
|
||||
"start": "node server.js"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@commaai/log_reader": "^0.8.0",
|
||||
"chai": "^4.3.4",
|
||||
"chai-http": "^4.3.0",
|
||||
"cookie-parser": "^1.4.5",
|
||||
"cors": "^2.8.5",
|
||||
"crypto": "^1.0.1",
|
||||
|
@ -25,10 +27,12 @@
|
|||
"htmlspecialchars": "^1.0.5",
|
||||
"jsonwebtoken": "^8.5.1",
|
||||
"log4js": "^6.3.0",
|
||||
"mocha": "^8.4.0",
|
||||
"multer": "^1.4.2",
|
||||
"proper-lockfile": "^4.1.2",
|
||||
"sendmail": "^1.6.1",
|
||||
"sqlite": "^4.0.22",
|
||||
"sqlite3": "^5.0.2"
|
||||
"sqlite3": "^5.0.2",
|
||||
"supertest": "^6.1.3"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -212,20 +212,20 @@ router.get('/v1.3/:dongleId/upload_url/', runAsyncWrapper(async (req, res) => {
|
|||
|
||||
|
||||
// DEVICE REGISTRATION OR RE-ACTIVATION
|
||||
router.post('/v2/pilotauth/', bodyParser.urlencoded({extended: true}), runAsyncWrapper(async (req, res) => {
|
||||
router.post('/v2/pilotauth/', bodyParser.urlencoded({extended: true}), async (req, res) => {
|
||||
var imei1 = req.query.imei;
|
||||
var serial = req.query.serial;
|
||||
var public_key = req.query.public_key;
|
||||
var register_token = req.query.register_token;
|
||||
|
||||
if (imei1 == null || imei1.length < 5 || serial == null || serial.length < 5 || public_key == null || public_key.length < 5 || register_token == null || register_token.length < 5) {
|
||||
logger.error("HTTP.V2.PILOTAUTH a required parameter is missing or empty");
|
||||
logger.error(`HTTP.V2.PILOTAUTH a required parameter is missing or empty ${JSON.stringify(req.query)}`);
|
||||
res.status(400);
|
||||
res.send('Malformed Request.');
|
||||
return;
|
||||
}
|
||||
var decoded = await controllers.authentication.validateJWT(req.query.register_token, public_key);
|
||||
|
||||
var decoded = controllers.authentication.validateJWT(req.query.register_token, public_key);
|
||||
|
||||
if (decoded == null || decoded.register == undefined) {
|
||||
logger.error("HTTP.V2.PILOTAUTH JWT token is invalid (" + JSON.stringify(decoded) + ")");
|
||||
|
@ -264,7 +264,7 @@ router.post('/v2/pilotauth/', bodyParser.urlencoded({extended: true}), runAsyncW
|
|||
res.json({dongle_id: device.dongle_id});
|
||||
|
||||
}
|
||||
}))
|
||||
})
|
||||
|
||||
|
||||
// RETRIEVES DATASET FOR OUR MODIFIED CABANA - THIS RESPONSE IS USED TO FAKE A DEMO ROUTE
|
||||
|
|
11
server.js
11
server.js
|
@ -71,7 +71,7 @@ const web = async () => {
|
|||
|
||||
|
||||
app.get('/', async (req, res) => {
|
||||
res.status(404);
|
||||
res.status(200);
|
||||
var response = '<html style="font-family: monospace"><h2>404 Not found</h2>' +
|
||||
'Are you looking for the <a href="/useradmin">useradmin dashboard</a>?';
|
||||
res.send(response);
|
||||
|
@ -113,15 +113,22 @@ lockfile.lock('retropilot_server.lock', {realpath: false, stale: 30000, update:
|
|||
|
||||
|
||||
|
||||
|
||||
httpServer.listen(config.httpPort, config.httpInterface, () => {
|
||||
logger.info(`Retropilot Server listening at http://` + config.httpInterface + `:` + config.httpPort)
|
||||
});
|
||||
httpsServer.listen(config.httpsPort, config.httpsInterface, () => {
|
||||
logger.info(`Retropilot Server listening at https://` + config.httpsInterface + `:` + config.httpsPort)
|
||||
});
|
||||
|
||||
})();
|
||||
|
||||
}).catch((e) => {
|
||||
console.error(e)
|
||||
process.exit();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
module.exports = app;
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
|
||||
const jwt = require('jsonwebtoken')
|
||||
const crypto = require('crypto')
|
||||
|
||||
const devicePrivateKey = "-----BEGIN RSA PRIVATE KEY-----\n" +
|
||||
"MIIEowIBAAKCAQEAwhH9PqBd/R/QPvcf1Gom5Vp+zYb1+DLjiFMC7a1lNvV8MUqK\n" +
|
||||
"cKVzboq/TjkKxkPUxRRjhgt4TmxhxJ6AHAOvONMvXtS1gm8EuiJbzSUDbgr6Y3PV\n" +
|
||||
"/jHQEb8tWcmM5UZ4TV+VPBmY4w9UWJbCiJW1Udn253bqil3Mv2D4WjpxlQDNGmpc\n" +
|
||||
"Aq0b7N20WoMt/DB3Z/AnixYKLGDLmHIe8Umq9btFPv/ulVexuzeoJoYjMZLDv4Sf\n" +
|
||||
"SE4ONmDqAacjTtBPaEFedlerKVMN0PI2IzDeGvEqif98lEEVh4/3X1UP21A2Cgiy\n" +
|
||||
"nHQn92HRTR8Xkc5EYDOYEpwi97G6g+qaFtOacQIDAQABAoIBAQCFtuVZGB+KPzg5\n" +
|
||||
"mgXZUkZ4cnC55YpmN5HkJOX4oycAxgWK5MQcNzMgcAK9v7m3v5bDL3gfLJn41t5K\n" +
|
||||
"HbdBFhzNt1yFJ2Pked+06+V6pE0HrhK1IWPJH8Mv5xw1KBSnCHXtQbVOUoivsak4\n" +
|
||||
"3K8ucpAa1GY1Nw8ExPpExmh3qpsFwPFFq3ZkDdGPaxQdOGzrNwC9Z6R+XNEdh+Ub\n" +
|
||||
"N6On3McK+AmI4deW5GWdL54vHsC0MfWhdWMklPcw98o9ZVQ9V6Bzf8tRIBVB4qRB\n" +
|
||||
"pyQaRRPmkpX0s5mNCAZljLxyO1oD0yfugSZOnbbmo47BmYQiNd1WfxVXwMqR0dNE\n" +
|
||||
"js4HCW7BAoGBAPrMOFlsYpP3DLOXSHQwt+ZGcnh54NJcz97KJKQRE93H4pVUcnsn\n" +
|
||||
"VhgNTq1bYkw8CdFpPJgQgeBeX4djDyfFYEDYQnmAi0hcIFuwEV8U8LYsp7EyLWVA\n" +
|
||||
"pR+vtj5mIkdZ/j4jsYAMrQbwbweptxWiOeGqGr7vGzxOXBLDS9W4FZNZAoGBAMYY\n" +
|
||||
"iU58TTWPdUllkvxPToXv6+tjognnbatYxzrwiRRKlAuc6JPZP3qADhJ7SZNxaB88\n" +
|
||||
"aun+GZEwOITCZHkKl5oSyshb0mp9SIWlG7Nkn08/8464eAJbk7tNwtdOAHdbzKS0\n" +
|
||||
"LXpNlQ9ZGy36vE6KtGctPfGY5H4r8uIX+SlmJNTZAoGAMDDnrv8tngL9tNCgAnuO\n" +
|
||||
"CriErHO26JUe+E9dZQ1HBPmwp0MX0GRJncuIz7TcmYt703pmQ04Ats1Li+dT9S9v\n" +
|
||||
"BGbJtzElEl1pdlTJsbyDWG4SNvFOWcNnN0R7P1g+w/kd6nDPXayR3uB6ZT2OSaDn\n" +
|
||||
"gF5AT2oAkMD53j0aqFF8C9kCgYB2pTN3wpMrxSRmNWP3ojhRmAUhEqd2bxoMSjvp\n" +
|
||||
"XS98674Hxo62HqQaZqAHCbhjisTmEHWod/wwLUVsnlE2/dUW/rJdlkFMboUFJoKU\n" +
|
||||
"y2tvN8pUbL/UCa1NvaE4+wrkciL7cr7aRaVFcAULYOVv1Tt/oGU9Umln+EKcj+c3\n" +
|
||||
"mGnu4QKBgBq7yEEj99q4BoK0DhS9t/Y/akN60rPrkOetxgbpSgvLifictFg9Og0p\n" +
|
||||
"empY8kk3cQACUIKoLkbrx7mOrC/MUFWZ7H4/65QxvJWsyVvdgD3JCuX6gntgxFLR\n" +
|
||||
"gELymgXiYG6TBxfH6xcFtNrFe6DeTv8YXrKRR50Kg8kjFpvmm5s9\n" +
|
||||
"-----END RSA PRIVATE KEY-----\n"
|
||||
const devicePubKey = "-----BEGIN PUBLIC KEY-----\n" +
|
||||
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwhH9PqBd/R/QPvcf1Gom\n" +
|
||||
"5Vp+zYb1+DLjiFMC7a1lNvV8MUqKcKVzboq/TjkKxkPUxRRjhgt4TmxhxJ6AHAOv\n" +
|
||||
"ONMvXtS1gm8EuiJbzSUDbgr6Y3PV/jHQEb8tWcmM5UZ4TV+VPBmY4w9UWJbCiJW1\n" +
|
||||
"Udn253bqil3Mv2D4WjpxlQDNGmpcAq0b7N20WoMt/DB3Z/AnixYKLGDLmHIe8Umq\n" +
|
||||
"9btFPv/ulVexuzeoJoYjMZLDv4SfSE4ONmDqAacjTtBPaEFedlerKVMN0PI2IzDe\n" +
|
||||
"GvEqif98lEEVh4/3X1UP21A2CgiynHQn92HRTR8Xkc5EYDOYEpwi97G6g+qaFtOa\n" +
|
||||
"cQIDAQAB\n" +
|
||||
"-----END PUBLIC KEY-----\n";
|
||||
const rougePublicKey = "-----BEGIN PUBLIC KEY-----\n" +
|
||||
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsD2nKi9wqmib8kEAuyz7\n" +
|
||||
"6N2OiL5ZpCkgai02V0G/cHUdkQXGxw0gWnYaDmY4uhgQM4W1jpHkwLW3PXavyDYE\n" +
|
||||
"mCeORRS2ChYqPpJJkSQ+MO1bkR1blhixF6O39gIH5+0ZuiqnDYJIcn+DcYJrTzCz\n" +
|
||||
"HXyPvRztFuuKp1unJRi8cSL6ljq5LMjZLsuY9Eb7JmYRsXB/xHDpXysyqq1VGD5c\n" +
|
||||
"QSCJMFzykQUe4PR3AhP05SunJMA+QNhRxKUVzXyo3bpAXsRhhRr/E/jl48E22edl\n" +
|
||||
"cgXar6R9CxyHY31jdJnd9pp2KPUnNgnTBdF2w3pdN9frS9QHCDLDvLbCgd2bibSj\n" +
|
||||
"CwIDAQAB\n" +
|
||||
"-----END PUBLIC KEY-----"
|
||||
|
||||
function makeJWT() {
|
||||
const token = jwt.sign({ register: true }, devicePrivateKey, { algorithm: 'RS256'});
|
||||
return `JWT ${token}`
|
||||
}
|
||||
|
||||
function getImei() {
|
||||
return parseInt(Math.random().toFixed(15).replace("0.",""))
|
||||
}
|
||||
|
||||
function getSerial() {
|
||||
return crypto.randomBytes(10).toString('hex');
|
||||
}
|
||||
|
||||
|
||||
|
||||
module.exports = {
|
||||
makeJWT, getImei, getSerial, rougePublicKey, devicePubKey, devicePrivateKey
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
const request = require('supertest');
|
||||
const dummyGenerator = require('./../dummyGenerator');
|
||||
let app;
|
||||
|
||||
|
||||
|
||||
module.exports = (server) => {
|
||||
app = server;
|
||||
|
||||
describe('/v2/pilotauth/ - Testing device registration', function() {
|
||||
it('Returns dongle ID on valid registration', function(done) {
|
||||
request(server)
|
||||
.post('/v2/pilotauth/')
|
||||
.query({
|
||||
imei: dummyGenerator.getImei(),
|
||||
serial: dummyGenerator.getSerial(),
|
||||
public_key: dummyGenerator.devicePubKey,
|
||||
register_token: dummyGenerator.makeJWT()
|
||||
})
|
||||
|
||||
.set('Accept', 'application/x-www-form-urlencoded')
|
||||
.expect('Content-Type', /json/)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
if (!res.body.dongle_id) throw new Error("API Failed to return dongle_id on status 200")
|
||||
})
|
||||
.end(done)
|
||||
});
|
||||
|
||||
it('Returns 400 when incorrect public key given', function(done) {
|
||||
request(server)
|
||||
.post('/v2/pilotauth/')
|
||||
.query({
|
||||
imei: dummyGenerator.getImei(),
|
||||
serial: dummyGenerator.getSerial(),
|
||||
public_key: dummyGenerator.rougePublicKey,
|
||||
register_token: dummyGenerator.makeJWT()
|
||||
})
|
||||
|
||||
.set('Accept', 'application/x-www-form-urlencoded')
|
||||
.expect('Content-Type', /text/)
|
||||
.expect(400)
|
||||
.end(done)
|
||||
});
|
||||
|
||||
it('Returns 400 when incorrect public key given', function(done) {
|
||||
request(server)
|
||||
.post('/v2/pilotauth/')
|
||||
.query({
|
||||
imei: dummyGenerator.getImei(),
|
||||
serial: dummyGenerator.getSerial(),
|
||||
public_key: dummyGenerator.rougePublicKey,
|
||||
register_token: ""
|
||||
})
|
||||
|
||||
.set('Accept', 'application/x-www-form-urlencoded')
|
||||
.expect('Content-Type', /text/)
|
||||
.expect(400)
|
||||
.end(done)
|
||||
});
|
||||
|
||||
it('Returns 400 when missing query', function(done) {
|
||||
request(server)
|
||||
.post('/v2/pilotauth/')
|
||||
|
||||
.set('Accept', 'application/x-www-form-urlencoded')
|
||||
.expect('Content-Type', /text/)
|
||||
.expect(400)
|
||||
.end(done)
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
var server = require('./../server')
|
||||
var request = require('supertest');
|
||||
|
||||
// TODO better way to only run tests once server is up
|
||||
describe('loading express', function () {
|
||||
it('responds to /', function testSlash(done) {
|
||||
request(server)
|
||||
.get('/')
|
||||
.expect(200, done);
|
||||
});
|
||||
it('404 everything else', function testPath(done) {
|
||||
request(server)
|
||||
.get('/foo/bar')
|
||||
.expect(404, done);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
require('./routes/api.test')(server);
|
Loading…
Reference in New Issue