diff --git a/lib/units/api/config/default.yaml b/lib/units/api/config/default.yaml index d289e535..360e0a84 100644 --- a/lib/units/api/config/default.yaml +++ b/lib/units/api/config/default.yaml @@ -6,7 +6,6 @@ swagger: fittingsDirs: [ fittings ] defaultPipe: null swaggerControllerPipe: swagger_controllers # defines the standard processing pipe for controllers - swagger: 'swagger/api_v1.yaml' # values defined in the bagpipes key are the bagpipes pipes and fittings definitions # (see https://github.com/apigee-127/bagpipes) @@ -22,10 +21,13 @@ swagger: name: swagger_validator validateResponse: true + _swagger_security: + name: swagger_security + securityHandlersModule: helpers/securityHandlers + # pipe for all swagger-node controllers swagger_controllers: - onError: json_error_handler - - cors - swagger_params_parser - swagger_security - _swagger_validate diff --git a/lib/units/api/helpers/securityHandlers.js b/lib/units/api/helpers/securityHandlers.js new file mode 100644 index 00000000..9dee2bd9 --- /dev/null +++ b/lib/units/api/helpers/securityHandlers.js @@ -0,0 +1,70 @@ +var jwtutil = require('../../../util/jwtutil') +var urlutil = require('../../../util/urlutil') +var logger = require('../../../util/logger') +var dbapi = require('../../../db/api') + +var log = logger.createLogger('api:auth') + +module.exports = { + accessTokenAuth: accessTokenAuth +} + +function accessTokenAuth(req, res, next) { + if (req.headers.authorization) { + var tokenId = req.headers.authorization.split(" ")[1] + + if (tokenId) { + dbapi.loadAccessToken(tokenId) + .then(function(token) { + var jwt = token.jwt + , data = jwtutil.decode(jwt, req.options.secret) + + if (data) { + dbapi.loadUser(data.email) + .then(function(user) { + if (user) { + req.user = user + next() + } + }) + } + }) + .catch(function(err) { + log.error('Failed to load token: ', err.stack) + res.json(500, { + success: false, + description: "Bad Access Token" + }) + }) + } else { + log.error("Bad Access Token Header") + res.json(500, { + success: false, + description: "Bad Access Token Header" + }) + } + } + // TODO: Remove this once frontend become stateless + else if (req.session && req.session.jwt) { + dbapi.loadUser(req.session.jwt.email) + .then(function(user) { + if (user) { + req.user = user + next() + } + else { + res.json(500, { + success: false, + description: "Bad Request" + }) + } + }) + .catch(next) + } + else { + res.json(500, { + success: false, + description: "Request does not have Authorization header" + }) + } +} diff --git a/lib/units/api/index.js b/lib/units/api/index.js index 10a3eb2c..dd9f51a4 100644 --- a/lib/units/api/index.js +++ b/lib/units/api/index.js @@ -7,8 +7,6 @@ var cookieSession = require('cookie-session') var logger = require('../../util/logger') -var auth = require('./middleware/auth') - module.exports = function(options) { var log = logger.createLogger('api') , app = express() @@ -25,17 +23,19 @@ module.exports = function(options) { swaggerExpress.register(app); }) + // Adding options in request, so that swagger controller + // can use it. + app.use(function(req, res, next) { + req.options = options + next() + }) + // TODO: Remove this once frontend is stateless app.use(cookieSession({ name: options.ssid , keys: [options.secret] })) - app.use(auth({ - secret: options.secret - , authUrl: options.authUrl - })) - server.listen(options.port) log.info('Listening on port %d', options.port) } diff --git a/lib/units/api/middleware/auth.js b/lib/units/api/middleware/auth.js deleted file mode 100644 index 742c5884..00000000 --- a/lib/units/api/middleware/auth.js +++ /dev/null @@ -1,65 +0,0 @@ -var jwtutil = require('../../../util/jwtutil') -var urlutil = require('../../../util/urlutil') -var logger = require('../../../util/logger') -var dbapi = require('../../../db/api') - -module.exports = function(options) { - return function(req, res, next) { - - var log = logger.createLogger('api:auth') - - if (req.headers.authorization) { - var tokenId = req.headers.authorization.split(" ")[1] - - if (tokenId) { - dbapi.loadAccessToken(tokenId) - .then(function(token) { - var jwt = token.jwt - , data = jwtutil.decode(jwt, options.secret) - - if (data) { - dbapi.loadUser(data.email) - .then(function(user) { - if (user) { - req.user = user - next() - } - }) - } - }) - .catch(function(err) { - log.error('Failed to load token: ', err.stack) - res.json(500, { - success: false, - description: "Bad Access Token" - }) - }) - } else { - log.error("Bad Access Token") - res.json(500, { - success: false, - description: "Bad Access Token Header" - }) - } - } - // TODO: Remove this once frontend become stateless - else if (req.session && req.session.jwt) { - dbapi.loadUser(req.session.jwt.email) - .then(function(user) { - if (user) { - req.user = user - next() - } - else { - // We no longer have the user in the database - res.redirect(options.authUrl) - } - }) - .catch(next) - } - else { - // No session, forward to auth client - res.redirect(options.authUrl) - } - } -} diff --git a/lib/units/api/swagger/api_v1.yaml b/lib/units/api/swagger/api_v1.yaml index e44c2b95..23df5f0d 100644 --- a/lib/units/api/swagger/api_v1.yaml +++ b/lib/units/api/swagger/api_v1.yaml @@ -1,14 +1,15 @@ swagger: "2.0" info: - version: "1.0.10" + version: "2.0.0" title: Smartphone Test Farm - description: Control and manager real Smartphone devices from browser and apis + description: Control and manager real Smartphone devices from browser and restful apis license: name: Apache-2.0 url: http://www.apache.org/licenses/LICENSE-2.0 contact: - url: http://openstf.io/ + name: STF Support email: contact@openstf.io + url: http://openstf.io/ basePath: /api/v1 schemes: - http @@ -17,6 +18,11 @@ consumes: - application/json produces: - application/json +tags: + - name: user + description: User Operations + - name: device + description: Device Operations paths: /me: x-swagger-router-controller: user @@ -24,6 +30,8 @@ paths: summary: User Profile description: The User Profile endpoint returns information about current authorized user. operationId: getCurrentUser + tags: + - user responses: "200": description: Current User Profile information @@ -33,6 +41,8 @@ paths: description: Unexpected Error schema: $ref: "#/definitions/ErrorResponse" + security: + - accessTokenAuth: [] # TODO: Change group endpoint with something more easy to understandable endpoint /group: x-swagger-router-controller: user @@ -40,21 +50,27 @@ paths: summary: User Group description: The User Group endpoint returns information about user group of current authorized user. operationId: getCurrentUserGroup + tags: + - user responses: "200": - description: Current User Group information + description: Current User's Group information schema: $ref: "#/definitions/GroupResponse" default: description: Unexpected Error schema: $ref: "#/definitions/ErrorResponse" + security: + - accessTokenAuth: [] /accessTokens: x-swagger-router-controller: token get: summary: Access Tokens - description: Return Current User Access Tokens titles + description: The Access Tokens endpoints returns titles of all the valid access tokens. operationId: getAccessTokens + tags: + - user responses: "200": description: Access Tokens titles @@ -64,12 +80,16 @@ paths: description: Unexpected Error schema: $ref: "#/definitions/ErrorResponse" + security: + - accessTokenAuth: [] /devices: x-swagger-router-controller: device get: summary: Device List - description: List of all the STF devices including Disconnected and Offline + description: The devices enpoint return list of all the STF devices including Disconnected and Offline operationId: getDevices + tags: + - device responses: "200": description: List of Devices @@ -79,12 +99,16 @@ paths: description: Unexpected Error schema: $ref: "#/definitions/ErrorResponse" + security: + - accessTokenAuth: [] /devices/{serial}: x-swagger-router-controller: device get: summary: Device Information - description: Device Information + description: The device enpoint return information about a single device. operationId: getDeviceBySerial + tags: + - device parameters: - name: serial in: path @@ -100,6 +124,8 @@ paths: description: Unexpected Error schema: $ref: "#/definitions/ErrorResponse" + security: + - accessTokenAuth: [] /swagger.json: x-swagger-pipe: swagger_raw definitions: @@ -145,3 +171,9 @@ definitions: properties: message: type: string + +securityDefinitions: + accessTokenAuth: + type: apiKey + name: accessTokenAuth + in: header