API authentification (tbc...)

This commit is contained in:
Gaël Métais
2014-12-09 09:04:28 +01:00
parent 906f0876a0
commit 49015e3422
9 changed files with 300 additions and 22 deletions
+31 -5
View File
@@ -1,5 +1,11 @@
module.exports = function(grunt) {
var DEV_SERVER_PORT = 8383;
var TEST_SERVER_PORT = 8387;
// Tell our Express server that Grunt launched it
process.env.GRUNTED = true;
// Project configuration.
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
@@ -77,7 +83,7 @@ module.exports = function(grunt) {
options: {
reporter: 'spec',
},
src: ['coverage/test/server/runsDatastoreTest.js']
src: ['coverage/test/api/apiTest.js']
},
coverage: {
options: {
@@ -85,11 +91,25 @@ module.exports = function(grunt) {
quiet: true,
captureFile: 'coverage/coverage.html'
},
src: ['coverage/test/api/*.js']
src: ['coverage/test/core/*.js', 'coverage/test/api/*.js']
}
},
express: {
test: {
dev: {
options: {
port: 8383,
server: './bin/server.js',
serverreload: true,
showStack: true
}
},
testServer: {
options: {
port: 8387,
server: './bin/server.js'
}
},
testSuite: {
options: {
port: 8388,
bases: 'test/www'
@@ -114,10 +134,15 @@ module.exports = function(grunt) {
'jshint'
]);
grunt.registerTask('dev', [
'express:dev'
]);
grunt.registerTask('test', [
'build',
'jshint',
'express:test',
'express:testServer',
'express:testSuite',
'clean:coverage',
'blanket',
'copy:coverage',
@@ -128,7 +153,8 @@ module.exports = function(grunt) {
grunt.registerTask('test-current-work', [
'build',
'jshint',
'express:test',
'express:testServer',
'express:testSuite',
'clean:coverage',
'blanket',
'copy:coverage',
+13 -4
View File
@@ -7,8 +7,11 @@ var server = require('http').createServer(app);
var bodyParser = require('body-parser');
var compress = require('compression');
var authMiddleware = require('../lib/server/authMiddleware');
app.use(compress());
app.use(bodyParser.json());
app.use(authMiddleware);
// Initialize the controllers
@@ -16,7 +19,13 @@ var apiController = require('../lib/server/controllers/apiController')
var uiController = require('../lib/server/controllers/uiController')(app);
// Launch the server
server.listen(settings.serverPort, function() {
console.log('Listening on port %d', server.address().port);
});
// Let's start the server!
if (!process.env.GRUNTED) {
// The server is not launched by Grunt
server.listen(settings.serverPort, function() {
console.log('Listening on port %d', server.address().port);
});
}
// For Grunt
module.exports = app;
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "yellowlabtools",
"dependencies": {
"angular": "~1.3.1",
"angular": "~1.3.5",
"ngModal": "git://github.com/gmetais/ngModal.git#1.2.3"
}
}
+52
View File
@@ -0,0 +1,52 @@
var config = require('../../server_config/settings.json');
var jwt = require('jwt-simple');
var debug = require('debug')('authMiddleware');
var authMiddleware = function(req, res, next) {
'use strict';
if (req.path.indexOf('/api/') === 0) {
// Test if it's an authorized key
if (req.headers && req.headers['x-api-key'] && isApiKeyValid(req.headers['x-api-key'])) {
next();
return;
}
// Test if it's an authorized token
if (req.headers && req.headers['x-api-token'] && isTokenValid(req.headers['x-api-token'])) {
next();
return;
}
res.status(401).send('Unauthorized');
}
};
function isApiKeyValid(apiKey) {
return (config.authorizedKeys[apiKey]) ? true : false;
}
function isTokenValid(token) {
var data = null;
try {
jwt.decode(token, config.tokenSalt);
} catch(err) {
debug('Error while decoding token');
debug(err);
return false;
}
return data.expire &&
data.expire > Date.now() &&
data.application &&
config.authorizedApplications.indexOf(data.application) >= 0;
}
module.exports = authMiddleware;
+7 -5
View File
@@ -13,10 +13,7 @@ var ApiController = function(app) {
var runsDatastore = new RunsDatastore();
var resultsDatastore = new ResultsDatastore();
// Retrieve the list of all runs
/*app.get('/api/runs', function(req, res) {
// NOT YET
});*/
// Create a new run
app.post('/api/runs', function(req, res) {
@@ -107,6 +104,11 @@ var ApiController = function(app) {
}
});
// Retrieve the list of all runs
/*app.get('/api/runs', function(req, res) {
// NOT YET
});*/
// Delete one run by id
/*app.delete('/api/runs/:id', function(req, res) {
deleteRun()
@@ -125,7 +127,7 @@ var ApiController = function(app) {
// Exists
app.head('/api/runs/:id', function(req, res) {
existsX();
// Retourne 200 si existe ou 404 si n'existe pas
// Returns 200 if the result exists or 404 if not
});
*/
+7 -5
View File
@@ -11,16 +11,17 @@
"main": "./lib/yellowlabtools.js",
"dependencies": {
"async": "~0.9.0",
"body-parser": "~1.9.2",
"compression": "~1.2.0",
"body-parser": "~1.10.0",
"compression": "~1.2.1",
"debug": "^2.1.0",
"express": "~4.10.4",
"jwt-simple": "^0.2.0",
"phantomas": "1.7.0",
"rimraf": "^2.2.8",
"socket.io": "~1.2.0"
},
"devDependencies": {
"chai": "^1.9.2",
"chai": "^1.10.0",
"grunt": "^0.4.5",
"grunt-blanket": "^0.0.8",
"grunt-contrib-clean": "^0.6.0",
@@ -29,11 +30,12 @@
"grunt-contrib-less": "^0.12.0",
"grunt-express": "^1.4.1",
"grunt-fontsmith": "^0.9.1",
"grunt-mocha-test": "^0.12.2",
"grunt-mocha-test": "^0.12.4",
"matchdep": "^0.3.0",
"mocha": "^2.0.1",
"phantomjs": "^1.9.10",
"phantomjs": "^1.9.12",
"q": "^1.1.2",
"request": "^2.49.0",
"sinon": "^1.12.1",
"sinon-chai": "^2.6.0"
},
+9 -1
View File
@@ -1,4 +1,12 @@
{
"serverPort": 80,
"googleAnalyticsId": "UA-54493828-1"
"googleAnalyticsId": "UA-54493828-1",
"authorized-keys": {
},
"tokenSalt": "",
"authorizedApplications": [
]
}
+9 -1
View File
@@ -1,4 +1,12 @@
{
"serverPort": 8383,
"googleAnalyticsId": ""
"googleAnalyticsId": "",
"authorizedKeys": {
"1234567890": "contact@gaelmetais.com"
},
"tokenSalt": "lake-city",
"authorizedApplications": [
"frontend"
]
}
+171
View File
@@ -0,0 +1,171 @@
var should = require('chai').should();
var request = require('request');
var jwt = require('jwt-simple');
var config = {
"authorizedKeys": {
"1234567890": "test@test.com"
},
"tokenSalt": "test-salt",
"authorizedApplications": ["wooot"]
};
var apiUrl = 'http://localhost:8387/api';
var wwwUrl = 'http://localhost:8388';
describe('api', function() {
var runId;
it('should not accept a query if there is no key in headers', function(done) {
this.timeout(5000);
request({
method: 'POST',
url: apiUrl + '/runs',
body: {
url: wwwUrl + '/simple-page.html',
waitForResponse: false
},
json: true
}, function(error, response, body) {
if (!error && response.statusCode === 401) {
done();
} else {
done(error || response.statusCode);
}
});
});
it('should refuse a query with an invalid key', function(done) {
this.timeout(5000);
request({
method: 'POST',
url: apiUrl + '/runs',
body: {
url: wwwUrl + '/simple-page.html',
waitForResponse: false
},
json: true,
headers: {
'X-Api-Key': 'invalid'
}
}, function(error, response, body) {
if (!error && response.statusCode === 401) {
done();
} else {
done(error || response.statusCode);
}
});
});
it('should accept a query with a valid key', function(done) {
this.timeout(5000);
request({
method: 'POST',
url: apiUrl + '/runs',
body: {
url: wwwUrl + '/simple-page.html',
waitForResponse: false
},
json: true,
headers: {
'X-Api-Key': Object.keys(config.authorizedKeys)[0]
}
}, function(error, response, body) {
if (!error && response.statusCode === 200) {
runId = body.runId;
runId.should.be.a('string');
done();
} else {
done(error || response.statusCode);
}
});
});
it('should refuse an expired token', function(done) {
this.timeout(5000);
request({
method: 'POST',
url: apiUrl + '/runs',
body: {
url: wwwUrl + '/simple-page.html',
waitForResponse: false
},
json: true,
headers: {
'X-Api-Token': jwt.encode({
application: config.authorizedApplications[0],
expire: Date.now() - 60000
}, config.tokenSalt)
}
}, function(error, response, body) {
if (!error && response.statusCode === 401) {
done();
} else {
done(error || response.statusCode);
}
});
});
it('should refuse a token from an unknown app', function(done) {
this.timeout(5000);
request({
method: 'POST',
url: apiUrl + '/runs',
body: {
url: wwwUrl + '/simple-page.html',
waitForResponse: false
},
json: true,
headers: {
'X-Api-Token': jwt.encode({
application: 'unknown-app',
expire: Date.now() + 60000
}, config.tokenSalt)
}
}, function(error, response, body) {
if (!error && response.statusCode === 401) {
done();
} else {
done(error || response.statusCode);
}
});
});
it('should accept a good token', function(done) {
this.timeout(5000);
request({
method: 'POST',
url: apiUrl + '/runs',
body: {
url: wwwUrl + '/simple-page.html',
waitForResponse: false
},
json: true,
headers: {
'X-Api-Token': jwt.encode({
application: config.authorizedApplications[0],
expire: Date.now() + 60000
}, config.tokenSalt)
}
}, function(error, response, body) {
if (!error && response.statusCode === 200) {
runId = body.runId;
runId.should.be.a('string');
done();
} else {
done(error || response.statusCode);
}
});
});
});