diff --git a/api/v1/controllers/token.js b/api/v1/controllers/token.js index 8ad5535894..7e872d9d32 100644 --- a/api/v1/controllers/token.js +++ b/api/v1/controllers/token.js @@ -7,52 +7,49 @@ */ /** - * api/v1/controllers/apiaccess.js + * api/v1/controllers/token.js */ - -const configuredPassport = require('../../../index').passportModule; const httpStatus = require('../constants').httpStatus; const u = require('../helpers/verbs/utils'); const apiErrors = require('../apiErrors'); const jwtUtil = require('../../../utils/jwtUtil'); +const helper = require('../helpers/nouns/tokens'); const resourceName = 'token'; module.exports = { /** - * Authenticates user and sends token in response with status code 200 - * if authenticated else responds with error. + * Authenticates user using provided token and creates new token with given + * name. Saves created token to db and sends token in response with status + * code 201 if token craeted, else responds with error. * @param {IncomingMessage} req - The request object * @param {ServerResponse} res - The response object * @param {Function} next - The next middleware function in the stack * */ - - // this endpoint will be replaced with new create token endpoint. postToken(req, res, next) { - configuredPassport.authenticate('local-login', (err, user/* , info */) => { - if (err) { - return u.handleError(next, err, resourceName); - } - - if (!user || !user.name) { - const loginErr = new apiErrors.LoginError({ - explanation: 'Invalid credentials.', - }); - loginErr.resource = resourceName; - return u.handleError(next, loginErr, resourceName); - } - - // just changing this to pass tests for now. - const createdToken = jwtUtil.createToken(user.name, user.name); - - return res.status(httpStatus.OK).json({ - success: true, - message: 'Enjoy your token!', - token: createdToken, + if (!req.user || !req.user.name) { + const tokenErr = new apiErrors.LoginError({ + explanation: 'Token not provided.', }); - })(req, res, next); + tokenErr.resource = resourceName; + return u.handleError(next, tokenErr, resourceName); + } + + const tokenName = req.swagger.params.queryBody.value.name; + + const tokenValue = jwtUtil.createToken(tokenName, req.user.name); + return helper.model.create({ + name: tokenName, + createdby: req.user.id, + }) + .then((createdToken) => { + const tokenObj = u.responsify(createdToken, helper, req.method); + tokenObj.token = tokenValue; + return res.status(httpStatus.CREATED).json(tokenObj); + }) + .catch((err) => u.handleError(next, err, helper.modelName)); }, }; // exports diff --git a/api/v1/helpers/nouns/tokens.js b/api/v1/helpers/nouns/tokens.js index edffa39a07..b79b6ffa01 100644 --- a/api/v1/helpers/nouns/tokens.js +++ b/api/v1/helpers/nouns/tokens.js @@ -19,9 +19,7 @@ module.exports = { apiLinks: { DELETE: `Delete this ${m}`, GET: `Retrieve this ${m}`, - PATCH: `Update selected attributes of this ${m}`, POST: `Create a new ${m}`, - PUT: `Overwrite all attributes of this ${m}`, }, baseUrl: '/v1/tokens', model: Token, diff --git a/api/v1/swagger.yaml b/api/v1/swagger.yaml index ff8a12538c..fe33b88448 100644 --- a/api/v1/swagger.yaml +++ b/api/v1/swagger.yaml @@ -4470,11 +4470,16 @@ paths: /token: x-swagger-router-controller: token post: - summary: Create an api access token + security: + - jwt: [] + summary: Create a new api access token tags: [ token ] description: >- - Authenticate a user with email and password and create an api access - token. + Create a new api access token by providing a token in header. + If the Refocus configuration parameter `useAccessToken` is set to + `true`, you must include an `Authorization` request header with your + [JSON Web Token](https://tools.ietf.org/html/rfc7519) (JWT) as the + value. You can get a token using `POST /v1/register` or `POST /v1/token`. operationId: postToken parameters: - @@ -4484,33 +4489,40 @@ paths: schema: type: object description: > - Credentials of person who interacts with the Refocus system. + Name of token. properties: - email: + name: type: string maxLength: 254 description: > - The user's email address. - password: - type: string - format: password - description: > - The user's password. + The name of token. Token name should be unique for a user. required: - - email - - password + - name responses: - 200: - description: >- - Success, returns a token. + 201: + description: Token created. schema: $ref: "#/definitions/TokenResponse" - 400: - $ref: "#/responses/400" + 401: + description: >- + Caller did not supply credentials or did not provide the correct + credentials. If you are using an API key, it may be invalid or your + Authorization header may be malformed. + schema: + $ref: '#/definitions/AuthenticationError' 403: - $ref: "#/responses/403" + description: >- + Caller is not authorized to create token. While your authentication + is valid, the authenticated user or token does not have permission + to perform this action. + schema: + $ref: '#/definitions/ErrorResponse' default: - $ref: "#/responses/genericError" + description: >- + An unexpected error occurred. Please review the response for error + details. + schema: + $ref: '#/definitions/ErrorResponse' # ============================================================================= definitions: @@ -5500,17 +5512,33 @@ definitions: Success: type: boolean readOnly: true - message: + id: + type: string + isDisabled: type: string readOnly: true - maxLength: 2082 - description: > - API access message. + name: + type: string + readOnly: true + description: + Name of token. token: type: string readOnly: true description: > API token. + createdBy: + readOnly: true + type: string + description: > + TODO + apiLinks: + readOnly: true + type: array + items: + $ref: "#/definitions/HATEOAS" + description: > + Hypertext As The Engine Of Application State. AuthenticationResponse: type: object diff --git a/tests/api/v1/globalconfig/delete.js b/tests/api/v1/globalconfig/delete.js index 3b8da0ef73..276ebaa9b1 100644 --- a/tests/api/v1/globalconfig/delete.js +++ b/tests/api/v1/globalconfig/delete.js @@ -19,11 +19,14 @@ const tu = require('../../../testUtils'); const u = require('./utils'); const path = '/v1/globalconfig'; const expect = require('chai').expect; +const jwtUtil = require('../../../../utils/jwtUtil'); describe(`api: DELETE ${path}`, () => { let testUserToken; - let predefinedAdminUserToken; let token; + const predefinedAdminUserToken = jwtUtil.createToken( + adminUser.name, adminUser.name + ); before((done) => { tu.createToken() @@ -51,31 +54,18 @@ describe(`api: DELETE ${path}`, () => { done(err); } else { testUserToken = res.body.token; - api.post('/v1/token') + api.post(path) + .set('Authorization', predefinedAdminUserToken) .send({ - username: adminUser.name, - email: adminUser.name, - password: adminUser.password, + key: `${tu.namePrefix}_GLOBAL_CONFIG_ABC`, + value: 'def', }) - .end((err2, res2) => { - if (err2) { - done(err2); + .expect(constants.httpStatus.CREATED) + .end((err3 /* , res3*/) => { + if (err3) { + done(err3); } else { - predefinedAdminUserToken = res2.body.token; - api.post(path) - .set('Authorization', predefinedAdminUserToken) - .send({ - key: `${tu.namePrefix}_GLOBAL_CONFIG_ABC`, - value: 'def', - }) - .expect(constants.httpStatus.CREATED) - .end((err3, res3) => { - if (err3) { - done(err3); - } else { - done(); - } - }); + done(); } }); } diff --git a/tests/api/v1/globalconfig/get.js b/tests/api/v1/globalconfig/get.js index f5f48af1a1..5e161aa5eb 100644 --- a/tests/api/v1/globalconfig/get.js +++ b/tests/api/v1/globalconfig/get.js @@ -19,11 +19,14 @@ const tu = require('../../../testUtils'); const u = require('./utils'); const path = '/v1/globalconfig'; const expect = require('chai').expect; +const jwtUtil = require('../../../../utils/jwtUtil'); describe(`api: GET ${path}`, () => { let testUserToken; - let predefinedAdminUserToken; let token; + const predefinedAdminUserToken = jwtUtil.createToken( + adminUser.name, adminUser.name + ); before((done) => { tu.createToken() @@ -51,28 +54,15 @@ describe(`api: GET ${path}`, () => { done(err); } else { testUserToken = res.body.token; - api.post('/v1/token') + api.post(path) + .set('Authorization', predefinedAdminUserToken) .send({ - username: adminUser.name, - email: adminUser.name, - password: adminUser.password, + key: `${tu.namePrefix}_GLOBAL_CONFIG_ABC`, + value: 'def', }) - .end((err2, res2) => { - if (err2) { - done(err2); - } else { - predefinedAdminUserToken = res2.body.token; - api.post(path) - .set('Authorization', predefinedAdminUserToken) - .send({ - key: `${tu.namePrefix}_GLOBAL_CONFIG_ABC`, - value: 'def', - }) - .expect(constants.httpStatus.CREATED) - .end((err3, res3) => { - done(); - }); - } + .expect(constants.httpStatus.CREATED) + .end((err3, res3) => { + done(); }); } }); diff --git a/tests/api/v1/globalconfig/patch.js b/tests/api/v1/globalconfig/patch.js index 485a72ae49..baf1a54b98 100644 --- a/tests/api/v1/globalconfig/patch.js +++ b/tests/api/v1/globalconfig/patch.js @@ -19,11 +19,14 @@ const tu = require('../../../testUtils'); const u = require('./utils'); const path = '/v1/globalconfig'; const expect = require('chai').expect; +const jwtUtil = require('../../../../utils/jwtUtil'); describe(`api: PATCH ${path}`, () => { let testUserToken; - let predefinedAdminUserToken; let token; + const predefinedAdminUserToken = jwtUtil.createToken( + adminUser.name, adminUser.name + ); before((done) => { tu.createToken() @@ -51,28 +54,15 @@ describe(`api: PATCH ${path}`, () => { done(err); } else { testUserToken = res.body.token; - api.post('/v1/token') + api.post(path) + .set('Authorization', predefinedAdminUserToken) .send({ - username: adminUser.name, - email: adminUser.name, - password: adminUser.password, + key: `${tu.namePrefix}_GLOBAL_CONFIG_ABC`, + value: 'def', }) - .end((err2, res2) => { - if (err2) { - done(err2); - } else { - predefinedAdminUserToken = res2.body.token; - api.post(path) - .set('Authorization', predefinedAdminUserToken) - .send({ - key: `${tu.namePrefix}_GLOBAL_CONFIG_ABC`, - value: 'def', - }) - .expect(constants.httpStatus.CREATED) - .end((err3, res3) => { - // - }); - } + .expect(constants.httpStatus.CREATED) + .end((err3, res3) => { + // }); done(); } diff --git a/tests/api/v1/globalconfig/post.js b/tests/api/v1/globalconfig/post.js index afcb82d30c..ddc688b4b8 100644 --- a/tests/api/v1/globalconfig/post.js +++ b/tests/api/v1/globalconfig/post.js @@ -19,11 +19,14 @@ const tu = require('../../../testUtils'); const u = require('./utils'); const path = '/v1/globalconfig'; const expect = require('chai').expect; +const jwtUtil = require('../../../../utils/jwtUtil'); describe(`api: POST ${path}`, () => { let testUserToken; - let predefinedAdminUserToken; let token; + const predefinedAdminUserToken = jwtUtil.createToken( + adminUser.name, adminUser.name + ); before((done) => { tu.createToken() @@ -51,19 +54,6 @@ describe(`api: POST ${path}`, () => { done(err); } else { testUserToken = res.body.token; - api.post('/v1/token') - .send({ - username: adminUser.name, - email: adminUser.name, - password: adminUser.password, - }) - .end((err2, res2) => { - if (err2) { - done(err2); - } else { - predefinedAdminUserToken = res2.body.token; - } - }); done(); } }); diff --git a/tests/api/v1/token/utils.js b/tests/api/v1/token/utils.js index 35ebb72370..7852b15316 100644 --- a/tests/api/v1/token/utils.js +++ b/tests/api/v1/token/utils.js @@ -23,6 +23,7 @@ module.exports = { forceDelete(done) { tu.forceDelete(tu.db.User, testStartTime) .then(() => tu.forceDelete(tu.db.Profile, testStartTime)) + .then(() => tu.forceDelete(tu.db.Token, testStartTime)) .then(() => done()) .catch((err) => done(err)); }, diff --git a/tests/api/v1/token/createToken.js b/tests/tokenReq/api/token/createToken.js similarity index 57% rename from tests/api/v1/token/createToken.js rename to tests/tokenReq/api/token/createToken.js index a3b5b8dba0..b4bc9a1fdd 100644 --- a/tests/api/v1/token/createToken.js +++ b/tests/tokenReq/api/token/createToken.js @@ -7,7 +7,7 @@ */ /** - * tests/api/v1/token/createToken.js + * tests/tokenReq/api/token/createtoken.js */ const expect = require('chai').expect; @@ -19,28 +19,27 @@ const registerPath = '/v1/register'; const tokenPath = '/v1/token'; describe('api: createToken', () => { + let defaultToken; before((done) => { api.post(registerPath) .send(u.fakeUserCredentials) - .end((err) => { + .end((err, res) => { if (err) { return done(err); } + + defaultToken = res.body.token; done(); }); }); after(u.forceDelete); - it('no user found', (done) => { + it('error if no token found provided in header', (done) => { api.post(tokenPath) - .send({ - email: 'unknown@abc.com', - password: 'fakePasswd', - username: 'nouser' - }) - .expect(constants.httpStatus.UNAUTHORIZED) - .expect(/LoginError/) + .send({ name: 'newToken' }) + .expect(constants.httpStatus.FORBIDDEN) + .expect(/No authorization token was found/) .end((err) => { if (err) { return done(err); @@ -50,15 +49,12 @@ describe('api: createToken', () => { }); }); - it('Wrong password', (done) => { + it('error if wrong token found provided', (done) => { api.post(tokenPath) - .send({ - email: 'user1@abc.com', - password: 'wrongPasswd', - username: 'user1' - }) - .expect(constants.httpStatus.UNAUTHORIZED) - .expect(/LoginError/) + .set('Authorization', `${defaultToken}xyz`) + .send({ name: 'newToken' }) + .expect(constants.httpStatus.FORBIDDEN) + .expect(/Invalid Token/) .end((err) => { if (err) { return done(err); @@ -68,20 +64,19 @@ describe('api: createToken', () => { }); }); - it('sucessful authentication, create token', (done) => { + it('sucessful authentication, create token for user', (done) => { api.post(tokenPath) - .send(u.fakeUserCredentials) - .expect(constants.httpStatus.OK) - .expect((res) => expect(res.body.success).to.be.true) - .expect((res) => expect(res.body.message).to.be.equal('Enjoy your token!')) - .expect((res) => expect(res.body).to.have.property('token')) - .end((err) => { + .set('Authorization', defaultToken) + .send({ name: 'newToken' }) + .expect(constants.httpStatus.CREATED) + .end((err, res) => { if (err) { return done(err); } + expect(res.body.name).to.be.equal('newToken'); + expect(res.body.isRevoked).to.be.equal('0'); done(); }); }); -}); - +}); \ No newline at end of file diff --git a/tests/tokenReq/api/token/utils.js b/tests/tokenReq/api/token/utils.js new file mode 100644 index 0000000000..b9c632f693 --- /dev/null +++ b/tests/tokenReq/api/token/utils.js @@ -0,0 +1,30 @@ +/** + * Copyright (c) 2016, salesforce.com, inc. + * All rights reserved. + * Licensed under the BSD 3-Clause license. + * For full license text, see LICENSE.txt file in the repo root or + * https://opensource.org/licenses/BSD-3-Clause + */ + +/** + * tests/tokenReq/api/token/utils.js + */ + +const tu = require('../../../testUtils'); + +const testStartTime = new Date(); + +module.exports = { + fakeUserCredentials: { + email: 'user1@abc.com', + password: 'fakePasswd', + username: 'user1' + }, + forceDelete(done) { + tu.forceDelete(tu.db.User, testStartTime) + .then(() => tu.forceDelete(tu.db.Profile, testStartTime)) + .then(() => tu.forceDelete(tu.db.Token, testStartTime)) + .then(() => done()) + .catch((err) => done(err)); + }, +};