Skip to content
Open
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
11 changes: 11 additions & 0 deletions lib/server/api/swagger/swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ consumes:
# format of the responses to the client (Accepts)
produces:
- application/json
securityDefinitions:
api:
type: apiKey
in: header
name: expertiseKey
paths:
/manifest:
x-swagger-router-controller: manifest
Expand All @@ -28,6 +33,8 @@ paths:
/nlu:
x-swagger-router-controller: nlu
get:
security:
- api: []
tags: [Resources]
summary: Return natural language understanding resource
description: Returns natural language undertsaning data for the specified
Expand Down Expand Up @@ -66,6 +73,8 @@ paths:
/converse:
x-swagger-router-controller: converse
post:
security:
- api: []
tags: [Converse]
summary: Converse with skill
description: Returns the skill response to a user query
Expand All @@ -86,6 +95,8 @@ paths:
/evaluate:
x-swagger-router-controller: evaluate-request
post:
security:
- api: []
tags: [Evaluate]
summary: Evaluate request confidence
description: Returns the skill's confidence for this request
Expand Down
38 changes: 38 additions & 0 deletions lib/server/security/api.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Licensed Materials - Property of IBM
* (c) Copyright IBM Corporation 2017. All Rights Reserved.
*/
const logger = require('winston');

/**
* Return a promise with the authentication of other "things" that can use this API
* @method authenticate
* @param {string} apikey the key that is passed into this api
* @return {object} if successfully passes security check, it passses
* control to the callback; otherwise passes error object to the callback
*/
function authenticate(apikey) {
//no authentication required so return true
if (!process.env.AUTHENTICATE_REQUESTS) {
return Promise.resolve(true);
}
//multiple keys in an array
if (process.env.API_KEY.split(',').indexOf(apikey) >= 0) {
return Promise.resolve(true);
}
return Promise.reject(new Error("Unauthorized access attempt. API Key missing or incorrect."));
}

module.exports = (req, securityDefinition, apiKey, cb) => {
authenticate(apiKey)
.then((result) => {
logger.info(`authenticated with apiKey ${apiKey}`);
cb();
})
.catch((err) => {
logger.error(err.message);
const error = new Error('Unauthorized access request. Incorrect API key.');
error.statusCode = 401;
cb(error);
});
};
84 changes: 5 additions & 79 deletions lib/server/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,10 @@ module.exports = app;

let config = {
appRoot: __dirname, // required config
configDir: __dirname + '/config'
configDir: __dirname + '/config',
swaggerSecurityHandlers: {
api: require('./security/api'),
}
};

// REST logging
Expand All @@ -35,40 +38,6 @@ app.use(morgan('short', {
}
}));

// Checks that the request for the expertise came from the core
app.all('/*', function (req, res, next) {
req.skillKey = req.headers.expertisekey;
// check only if defined to be a secured expertise
if (process.env.AUTHENTICATE_REQUESTS && JSON.parse(process.env.AUTHENTICATE_REQUESTS.toLowerCase())) {
let coreKey = "";
let valid = false;
let isConverse = (req.url.indexOf('converse') > 0);
let isEvaluate = (req.url.indexOf('evaluate') > 0);
let isDocs = (req.url.indexOf('api-docs') > 0);
// let is_manifest = (req.url.indexOf('manifest') > 0);
// let is_healthcheck = (req.url.indexOf('healthcheck') > 0);
if (isConverse || isDocs || isEvaluate) {
coreKey = getKey(req);
valid = authenticate(coreKey);
}
else {
valid = true;
}
// check if key is in the list of key and is not undefined
// let is_manifest = req.url.indexOf('manifest');
if (!valid) {
logger.info('authentication failed, key: ' + coreKey + "\n request url: " + req.url);
res.send(401)
} else {
logger.info('authentication succeeded, key: ' + coreKey + "\n request url: " + req.url);
next();
}
}
else {
next();
}
});

SwaggerExpress.create(config, function (err, swaggerExpress) {

if (err) {
Expand All @@ -90,47 +59,4 @@ SwaggerExpress.create(config, function (err, swaggerExpress) {
app.listen(port);


});
let getKeys = function() {
return fs.readFileSync(path.join(process.env.skillSDKResDir, assetsFolder, keysFileName), 'utf8', function(err, contents) {
if(err){
logger.info('could not read keys file, error: ' + err);
return "";
}
return contents;
}).split('\n');
};

let getKey = function (req) {
let key = req.header('expertiseKey') || req.query['api_key'];

if (!key) {
let query;

let referer = req.headers.referer;
if (referer) {
let parsedUrl = url.parse(req.headers.referer);
query = querystring.parse(parsedUrl.query);
} else {
query = req.query;
}

key = query['api_key'];
}

return (key);
};



let authenticate = function(core_key) {
// check if the core key is found in the list of keys
let keys = getKeys();
for (let i=0; i < keys.length; ++i) {
if(keys[i] === core_key) {
return true;
}
}
logger.info('key does not match');
return false;
};
});
Loading