/* jslint node:true */ /* global it:false */ /* global describe:false */ /* global before:false */ /* global after:false */ 'use strict'; const common = require('./common.js'), expect = require('expect.js'), speakeasy = require('speakeasy'), superagent = require('superagent'), fs = require('fs'), path = require('path'), paths = require('../../paths.js'), tokens = require('../../tokens.js'); describe('Profile API', function () { const { setup, cleanup, serverUrl, owner, user } = common; before(setup); after(cleanup); describe('get profile', function () { it('fails without token', async function () { const response = await superagent.get(`${serverUrl}/api/v1/profile`) .ok(() => true); expect(response.statusCode).to.equal(401); }); it('fails with empty token', async function () { const response = await superagent.get(`${serverUrl}/api/v1/profile`) .query({ access_token: '' }) .ok(() => true); expect(response.statusCode).to.equal(401); }); it('fails with invalid token', async function () { const response = await superagent.get(`${serverUrl}/api/v1/profile`) .query({ access_token: 'some token' }) .ok(() => true); expect(response.statusCode).to.equal(401); }); it('succeeds', async function () { const response = await superagent.get(`${serverUrl}/api/v1/profile`) .query({ access_token: owner.token }); expect(response.statusCode).to.equal(200); expect(response.body.username).to.equal(owner.username.toLowerCase()); expect(response.body.email).to.equal(owner.email.toLowerCase()); expect(response.body.fallbackEmail).to.equal(''); expect(response.body.displayName).to.be.a('string'); expect(response.body.password).to.not.be.ok(); expect(response.body.salt).to.not.be.ok(); expect(response.body.language).to.be(''); }); it('fails with expired token', async function () { const token = await tokens.add({ identifier: '0', clientId: 'clientid-0', expires: Date.now() - 2000 }); expect(token.accessToken).to.be.a('string'); const response = await superagent.get(`${serverUrl}/api/v1/profile`) .query({ access_token: token.accessToken }) .ok(() => true); expect(response.statusCode).to.equal(401); }); it('fails with invalid token in auth header', async function () { const response = await superagent.get(`${serverUrl}/api/v1/profile`) .set('Authorization', 'Bearer ' + 'x' + owner.token) .ok(() => true); expect(response.statusCode).to.equal(401); }); it('succeeds with token in auth header', async function () { const response = await superagent.get(`${serverUrl}/api/v1/profile`).set('Authorization', 'Bearer ' + owner.token); expect(response.statusCode).to.equal(200); expect(response.body.username).to.equal(owner.username.toLowerCase()); expect(response.body.email).to.equal(owner.email.toLowerCase()); expect(response.body.displayName).to.be.a('string'); expect(response.body.password).to.not.be.ok(); expect(response.body.salt).to.not.be.ok(); }); }); describe('email', function () { it('change email fails due to missing token', async function () { const response = await superagent.post(`${serverUrl}/api/v1/profile/email`) .send({ email: 'newemail@example.com' }) .ok(() => true); expect(response.statusCode).to.equal(401); }); it('change email fails due to missing password', async function () { const response = await superagent.post(`${serverUrl}/api/v1/profile/email`) .query({ access_token: owner.token }) .send({ email: 'newemail@example.com' }) .ok(() => true); expect(response.statusCode).to.equal(400); }); it('change email fails due to invalid password', async function () { const response = await superagent.post(`${serverUrl}/api/v1/profile/email`) .query({ access_token: owner.token }) .send({ email: 'foo@bar.com', password: 'this is wrong' }) .ok(() => true); expect(response.statusCode).to.equal(412); }); it('change email fails due to invalid email', async function () { const response = await superagent.post(`${serverUrl}/api/v1/profile/email`) .query({ access_token: owner.token }) .send({ email: 'foo@bar' }) .ok(() => true); expect(response.statusCode).to.equal(400); }); it('change email succeeds', async function () { const response = await superagent.post(`${serverUrl}/api/v1/profile/email`) .query({ access_token: owner.token }) .send({ email: 'newemail@example.Com', password: owner.password }); expect(response.statusCode).to.equal(204); const response2 = await superagent.get(`${serverUrl}/api/v1/profile`) .query({ access_token: owner.token }); expect(response2.statusCode).to.equal(200); expect(response2.body.username).to.equal(owner.username); expect(response2.body.email).to.equal('newemail@example.com'); // lower cased expect(response2.body.displayName).to.equal(''); }); }); describe('fallbackEmail', function () { it('change fallback email fails due to missing password', async function () { const response = await superagent.post(`${serverUrl}/api/v1/profile/fallback_email`) .query({ access_token: owner.token }) .send({ fallbackEmail: 'newemail@example.com' }) .ok(() => true); expect(response.statusCode).to.equal(400); }); it('change fallback email fails due to invalid password', async function () { const response = await superagent.post(`${serverUrl}/api/v1/profile/fallback_email`) .query({ access_token: owner.token }) .send({ fallbackEmail: 'foo@bar.com', password: 'this is wrong' }) .ok(() => true); expect(response.statusCode).to.equal(412); }); it('change fallback email succeeds', async function () { const response = await superagent.post(`${serverUrl}/api/v1/profile/fallback_email`) .query({ access_token: owner.token }) .send({ fallbackEmail: 'NewFallbackemail@example.com', password: owner.password }); expect(response.statusCode).to.equal(204); const response2 = await superagent.get(`${serverUrl}/api/v1/profile`) .query({ access_token: owner.token }); expect(response2.statusCode).to.equal(200); expect(response2.body.username).to.equal(owner.username); expect(response2.body.fallbackEmail).to.equal('newfallbackemail@example.com'); // lowercase }); }); describe('displayName', function () { it('change displayName succeeds', async function () { const response = await superagent.post(`${serverUrl}/api/v1/profile/display_name`) .query({ access_token: owner.token }) .send({ displayName: 'Agent Smith' }); expect(response.statusCode).to.equal(204); const response2 = await superagent.get(`${serverUrl}/api/v1/profile`) .query({ access_token: owner.token }); expect(response2.statusCode).to.equal(200); expect(response2.body.username).to.equal(owner.username); expect(response2.body.email).to.equal('newemail@example.com'); // lower cased expect(response2.body.displayName).to.equal('Agent Smith'); }); }); describe('password change', function () { it('fails due to missing current password', async function () { const response = await superagent.post(`${serverUrl}/api/v1/profile/password`) .query({ access_token: owner.token }) .send({ newPassword: 'some wrong password' }) .ok(() => true); expect(response.statusCode).to.equal(400); }); it('fails due to missing new password', async function () { const response = await superagent.post(`${serverUrl}/api/v1/profile/password`) .query({ access_token: owner.token }) .send({ password: owner.password }) .ok(() => true); expect(response.statusCode).to.equal(400); }); it('fails due to wrong password', async function () { const response = await superagent.post(`${serverUrl}/api/v1/profile/password`) .query({ access_token: owner.token }) .send({ password: 'some wrong password', newPassword: 'MOre#$%34' }) .ok(() => true); expect(response.statusCode).to.equal(412); }); it('fails due to invalid password', async function () { const response = await superagent.post(`${serverUrl}/api/v1/profile/password`) .query({ access_token: owner.token }) .send({ password: owner.password, newPassword: 'five' }) .ok(() => true); expect(response.statusCode).to.equal(400); }); it('succeeds', async function () { const response = await superagent.post(`${serverUrl}/api/v1/profile/password`) .query({ access_token: owner.token }) .send({ password: owner.password, newPassword: 'MOre#$%34' }); expect(response.statusCode).to.equal(204); }); }); describe('2fa login', function () { let secret; it('can get secret', async function () { const response = await superagent.post(`${serverUrl}/api/v1/profile/twofactorauthentication_secret`) .query({ access_token: user.token }); secret = response.body.secret; }); it('can enable 2fa', async function () { const totpToken = speakeasy.totp({ secret, encoding: 'base32' }); await superagent.post(`${serverUrl}/api/v1/profile/twofactorauthentication_enable`) .query({ access_token: user.token }) .send({ totpToken }); }); it('fails due to missing token', async function () { const response = await superagent.post(`${serverUrl}/api/v1/auth/login`) .send({ username: user.username, password: user.password }) .ok(() => true); expect(response.statusCode).to.equal(401); }); it('fails due to wrong token', async function () { const response = await superagent.post(`${serverUrl}/api/v1/auth/login`) .send({ username: user.username, password: user.password, totpToken: '12345' }) .ok(() => true); expect(response.statusCode).to.equal(401); }); it('succeeds', async function () { const totpToken = speakeasy.totp({ secret: secret, encoding: 'base32' }); const response = await superagent.post(`${serverUrl}/api/v1/auth/login`) .send({ username: user.username, password: user.password, totpToken: totpToken }); expect(response.statusCode).to.equal(200); expect(response.body).to.be.an(Object); expect(response.body.accessToken).to.be.a('string'); }); it('can disable 2fa', async function () { await superagent.post(`${serverUrl}/api/v1/profile/twofactorauthentication_disable`) .query({ access_token: user.token }) .send({ password: user.password }); }); it('did disable 2fa', async function () { const response = await superagent.post(`${serverUrl}/api/v1/auth/login`) .send({ username: user.username, password: user.password }); expect(response.statusCode).to.equal(200); expect(response.body).to.be.an(Object); expect(response.body.accessToken).to.be.a('string'); }); }); describe('avatar', function () { let customAvatarSize = 0; it('placeholder by default', async function () { const defaultAvatar = fs.readFileSync(path.join(paths.DASHBOARD_DIR, '/img/avatar-default-symbolic.svg')); const response = await superagent.get(`${serverUrl}/api/v1/profile/avatar/${user.id}`); expect(response.headers['content-type']).to.equal('image/svg+xml'); expect(response.body).to.eql(defaultAvatar); }); it('can set custom avatar', async function () { const response = await superagent.post(`${serverUrl}/api/v1/profile/avatar`) .query({ access_token: user.token }) .attach('avatar', './logo.png'); customAvatarSize = require('fs').readFileSync('./logo.png').length; expect(response.statusCode).to.be(202); }); it('did set custom avatar', async function () { const response = await superagent.get(`${serverUrl}/api/v1/profile`) .query({ access_token: user.token }); expect(response.body.avatarUrl).to.contain('/api/v1/profile/avatar/'); const response2 = await superagent.get(`${serverUrl}/api/v1/profile/avatar/${user.id}`) .ok(() => true); expect(parseInt(response2.headers['content-length'])).to.equal(customAvatarSize); expect(response2.statusCode).to.equal(200); }); it('can set gravatar', async function () { const response = await superagent.post(`${serverUrl}/api/v1/profile/avatar`) .query({ access_token: user.token }) .send({ avatar: 'gravatar' }); expect(response.statusCode).to.be(202); }); it('did set gravatar', async function () { const response = await superagent.get(`${serverUrl}/api/v1/profile`) .query({ access_token: user.token }); expect(response.body.avatarType).to.contain('gravatar'); }); it('can unset avatar', async function () { const response = await superagent.post(`${serverUrl}/api/v1/profile/avatar`) .query({ access_token: user.token }) .send({ avatar: '' }); expect(response.statusCode).to.be(202); }); it('did unset avatar', async function () { const defaultAvatar = fs.readFileSync(path.join(paths.DASHBOARD_DIR, '/img/avatar-default-symbolic.svg')); const response = await superagent.get(`${serverUrl}/api/v1/profile/avatar/${user.id}`); expect(response.headers['content-type']).to.equal('image/svg+xml'); expect(response.body).to.eql(defaultAvatar); }); }); describe('language', function () { it('fails to set unknown language', async function () { const response = await superagent.post(`${serverUrl}/api/v1/profile/language`) .query({ access_token: user.token }) .send({ language: 'ta' }) .ok(() => true); expect(response.statusCode).to.be(400); }); it('fails to set bad language', async function () { const response = await superagent.post(`${serverUrl}/api/v1/profile/language`) .query({ access_token: user.token }) .send({ language: 123 }) .ok(() => true); expect(response.statusCode).to.be(400); }); it('fails to set unknown language', async function () { const response = await superagent.post(`${serverUrl}/api/v1/profile/language`) .query({ access_token: user.token }) .send({ language: 'ta' }) .ok(() => true); expect(response.statusCode).to.be(400); }); it('set valid language', async function () { const response = await superagent.post(`${serverUrl}/api/v1/profile/language`) .query({ access_token: user.token }) .send({ language: 'en' }); expect(response.statusCode).to.be(204); }); it('did set language', async function () { const response = await superagent.get(`${serverUrl}/api/v1/profile`).query({ access_token: user.token }); expect(response.body.language).to.contain('en'); }); it('reset valid language', async function () { const response = await superagent.post(`${serverUrl}/api/v1/profile/language`) .query({ access_token: user.token }) .send({ language: '' }); expect(response.statusCode).to.be(204); }); it('did reset language', async function () { const response = await superagent.get(`${serverUrl}/api/v1/profile`).query({ access_token: user.token }); expect(response.body.language).to.contain(''); }); }); });