async'ify avatar and apppassword code

This commit is contained in:
Girish Ramakrishnan
2021-06-25 22:11:17 -07:00
parent 31d742fa67
commit 147c8df6e3
11 changed files with 385 additions and 354 deletions
+21 -22
View File
@@ -7,55 +7,54 @@ exports = module.exports = {
add
};
var assert = require('assert'),
const appPasswords = require('../apppasswords.js'),
assert = require('assert'),
BoxError = require('../boxerror.js'),
HttpError = require('connect-lastmile').HttpError,
HttpSuccess = require('connect-lastmile').HttpSuccess,
users = require('../users.js');
safe = require('safetydance');
function get(req, res, next) {
async function get(req, res, next) {
assert.strictEqual(typeof req.user, 'object');
assert.strictEqual(typeof req.params.id, 'string');
users.getAppPassword(req.params.id, function (error, result) {
if (error) return next(BoxError.toHttpError(error));
const [error, result] = await safe(appPasswords.get(req.params.id));
if (error) return next(BoxError.toHttpError(error));
if (!result) return next(new HttpError(404, 'appPassword not found'));
next(new HttpSuccess(200, result));
});
next(new HttpSuccess(200, appPasswords.removePrivateFields(result)));
}
function add(req, res, next) {
async function add(req, res, next) {
assert.strictEqual(typeof req.user, 'object');
assert.strictEqual(typeof req.body, 'object');
if (typeof req.body.name !== 'string') return next(new HttpError(400, 'name must be string'));
if (typeof req.body.identifier !== 'string') return next(new HttpError(400, 'identifier must be string'));
users.addAppPassword(req.user.id, req.body.identifier, req.body.name, function (error, result) {
if (error) return next(BoxError.toHttpError(error));
const [error, result] = await safe(appPasswords.add(req.user.id, req.body.identifier, req.body.name));
if (error) return next(BoxError.toHttpError(error));
next(new HttpSuccess(201, result));
});
next(new HttpSuccess(201, { id: result.id, password: result.password }));
}
function list(req, res, next) {
async function list(req, res, next) {
assert.strictEqual(typeof req.user, 'object');
users.getAppPasswords(req.user.id, function (error, result) {
if (error) return next(BoxError.toHttpError(error));
let [error, result] = await safe(appPasswords.list(req.user.id));
if (error) return next(BoxError.toHttpError(error));
next(new HttpSuccess(200, { appPasswords: result }));
});
result = result.map(appPasswords.removePrivateFields);
next(new HttpSuccess(200, { appPasswords: result }));
}
function del(req, res, next) {
async function del(req, res, next) {
assert.strictEqual(typeof req.user, 'object');
assert.strictEqual(typeof req.params.id, 'string');
// TODO: verify userId owns the id ?
users.delAppPassword(req.params.id, function (error) {
if (error) return next(BoxError.toHttpError(error));
const [error] = await safe(appPasswords.del(req.params.id));
if (error) return next(BoxError.toHttpError(error));
next(new HttpSuccess(204, {}));
});
next(new HttpSuccess(204, {}));
}
+29 -31
View File
@@ -13,7 +13,7 @@ exports = module.exports = {
disableTwoFactorAuthentication,
};
var assert = require('assert'),
const assert = require('assert'),
auditSource = require('../auditsource.js'),
BoxError = require('../boxerror.js'),
HttpError = require('connect-lastmile').HttpError,
@@ -35,24 +35,24 @@ function authorize(req, res, next) {
});
}
function get(req, res, next) {
async function get(req, res, next) {
assert.strictEqual(typeof req.user, 'object');
users.getAvatarUrl(req.user, function (error, avatarUrl) {
if (error) return next(BoxError.toHttpError(error));
const [error, avatarUrl] = await safe(users.getAvatarUrl(req.user));
if (error) return next(BoxError.toHttpError(error));
if (!avatarUrl) return next(new HttpError(404, 'User not found'));
next(new HttpSuccess(200, {
id: req.user.id,
username: req.user.username,
email: req.user.email,
fallbackEmail: req.user.fallbackEmail,
displayName: req.user.displayName,
twoFactorAuthenticationEnabled: req.user.twoFactorAuthenticationEnabled,
role: req.user.role,
source: req.user.source,
avatarUrl
}));
});
next(new HttpSuccess(200, {
id: req.user.id,
username: req.user.username,
email: req.user.email,
fallbackEmail: req.user.fallbackEmail,
displayName: req.user.displayName,
twoFactorAuthenticationEnabled: req.user.twoFactorAuthenticationEnabled,
role: req.user.role,
source: req.user.source,
avatarUrl
}));
}
function update(req, res, next) {
@@ -72,7 +72,7 @@ function update(req, res, next) {
});
}
function setAvatar(req, res, next) {
async function setAvatar(req, res, next) {
assert.strictEqual(typeof req.user, 'object');
if (!req.files.avatar) return next(new HttpError(400, 'avatar is missing'));
@@ -80,31 +80,29 @@ function setAvatar(req, res, next) {
const avatar = safe.fs.readFileSync(req.files.avatar.path);
if (!avatar) return next(BoxError.toHttpError(new BoxError(BoxError.FS_ERROR, safe.error.message)));
users.setAvatar(req.user.id, avatar, function (error) {
if (error) return next(BoxError.toHttpError(error));
const [error] = await safe(users.setAvatar(req.user.id, avatar));
if (error) return next(BoxError.toHttpError(error));
next(new HttpSuccess(202, {}));
});
next(new HttpSuccess(202, {}));
}
function clearAvatar(req, res, next) {
async function clearAvatar(req, res, next) {
assert.strictEqual(typeof req.user, 'object');
users.setAvatar(req.user.id, null, function (error) {
if (error) return next(BoxError.toHttpError(error));
const [error] = await safe(users.setAvatar(req.user.id, null));
if (error) return next(BoxError.toHttpError(error));
next(new HttpSuccess(202, {}));
});
next(new HttpSuccess(202, {}));
}
function getAvatar(req, res, next) {
async function getAvatar(req, res, next) {
assert.strictEqual(typeof req.params.identifier, 'string');
users.getAvatar(req.params.identifier, function (error, avatar) {
if (error) return next(BoxError.toHttpError(error));
const [error, avatar] = await safe(users.getAvatar(req.params.identifier));
if (error) return next(BoxError.toHttpError(error));
if (!avatar) return next(new HttpError(404, 'User not found'));
res.send(avatar);
});
res.send(avatar);
}
function changePassword(req, res, next) {
+80
View File
@@ -0,0 +1,80 @@
/* 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'),
superagent = require('superagent');
describe('App Passwords', function () {
const { setup, cleanup, serverUrl, user } = common;
before(setup);
after(cleanup);
describe('app password', function () {
it('cannot add app password with invalid token', async function () {
const response = await superagent.post(`${serverUrl}/api/v1/app_passwords`)
.query({ access_token: user.token + 'xx' })
.send({ name: 'my-device', identifier: 'someapp' })
.ok(() => true);
expect(response.statusCode).to.equal(401);
});
it('cannot add app password without name', async function () {
const response = await superagent.post(`${serverUrl}/api/v1/app_passwords`)
.query({ access_token: user.token })
.send({ identifier: 'someapp' })
.ok(() => true);
expect(response.statusCode).to.equal(400);
});
let pwd;
it('can add app password', async function () {
const response = await superagent.post(`${serverUrl}/api/v1/app_passwords`)
.query({ access_token: user.token })
.send({ name: 'my-device', identifier: 'someapp' });
expect(response.statusCode).to.equal(201);
expect(response.body.password).to.be.a('string');
pwd = response.body;
});
it('can get app passwords', async function () {
const response = await superagent.get(`${serverUrl}/api/v1/app_passwords`)
.query({ access_token: user.token });
expect(response.statusCode).to.equal(200);
expect(response.body.appPasswords).to.be.an(Array);
expect(response.body.appPasswords.length).to.be(1);
expect(response.body.appPasswords[0].name).to.be('my-device');
expect(response.body.appPasswords[0].identifier).to.be('someapp');
expect(response.body.appPasswords[0].hashedPassword).to.be(undefined);
expect(response.body.appPasswords[0].password).to.be(undefined);
});
it('can get app password', async function () {
const response = await superagent.get(`${serverUrl}/api/v1/app_passwords/${pwd.id}`)
.query({ access_token: user.token });
expect(response.statusCode).to.equal(200);
expect(response.body.name).to.be('my-device');
expect(response.body.identifier).to.be('someapp');
expect(response.body.hashedPassword).to.be(undefined);
expect(response.body.password).to.be(undefined);
});
it('can del app password', async function () {
const response = await superagent.del(`${serverUrl}/api/v1/app_passwords/${pwd.id}`)
.query({ access_token: user.token });
expect(response.statusCode).to.equal(204);
});
});
});
+31 -43
View File
@@ -258,65 +258,53 @@ describe('Profile API', function () {
});
});
describe('app password', function () {
it('cannot add app password with invalid token', async function () {
const response = await superagent.post(`${serverUrl}/api/v1/app_passwords`)
.query({ access_token: user.token + 'xx' })
.send({ name: 'my-device', identifier: 'someapp' })
describe('avatar', function () {
it('has no avatar by default (public route)', async function () {
const response = await superagent.get(`${serverUrl}/api/v1/profile`)
.query({ access_token: user.token });
expect(response.body.avatarUrl).to.contain('www.gravatar.com');
const response2 = await superagent.get(`${serverUrl}/api/v1/profile/avatar/${user.id}`)
.ok(() => true);
expect(response.statusCode).to.equal(401);
expect(response2.statusCode).to.equal(404);
});
it('cannot add app password without name', async function () {
const response = await superagent.post(`${serverUrl}/api/v1/app_passwords`)
it('can set avatar', async function () {
const response = await superagent.post(`${serverUrl}/api/v1/profile/avatar`)
.query({ access_token: user.token })
.send({ identifier: 'someapp' })
.attach('avatar', './logo.png');
expect(response.statusCode).to.be(202);
});
it('did set 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(response.statusCode).to.equal(400);
expect(response2.statusCode).to.equal(200);
});
let pwd;
it('can add app password', async function () {
const response = await superagent.post(`${serverUrl}/api/v1/app_passwords`)
.query({ access_token: user.token })
.send({ name: 'my-device', identifier: 'someapp' });
expect(response.statusCode).to.equal(201);
expect(response.body.password).to.be.a('string');
pwd = response.body;
});
it('can get app passwords', async function () {
const response = await superagent.get(`${serverUrl}/api/v1/app_passwords`)
it('can clear avatar', async function () {
const response = await superagent.del(`${serverUrl}/api/v1/profile/avatar`)
.query({ access_token: user.token });
expect(response.statusCode).to.equal(200);
expect(response.body.appPasswords).to.be.an(Array);
expect(response.body.appPasswords.length).to.be(1);
expect(response.body.appPasswords[0].name).to.be('my-device');
expect(response.body.appPasswords[0].identifier).to.be('someapp');
expect(response.body.appPasswords[0].hashedPassword).to.be(undefined);
expect(response.body.appPasswords[0].password).to.be(undefined);
expect(response.statusCode).to.be(202);
});
it('can get app password', async function () {
const response = await superagent.get(`${serverUrl}/api/v1/app_passwords/${pwd.id}`)
it('did clear avatar', async function () {
const response = await superagent.get(`${serverUrl}/api/v1/profile`)
.query({ access_token: user.token });
expect(response.body.avatarUrl).to.contain('www.gravatar.com');
expect(response.statusCode).to.equal(200);
expect(response.body.name).to.be('my-device');
expect(response.body.identifier).to.be('someapp');
expect(response.body.hashedPassword).to.be(undefined);
expect(response.body.password).to.be(undefined);
});
const response2 = await superagent.get(`${serverUrl}/api/v1/profile/avatar/${user.id}`)
.ok(() => true);
it('can del app password', async function () {
const response = await superagent.del(`${serverUrl}/api/v1/app_passwords/${pwd.id}`)
.query({ access_token: user.token });
expect(response.statusCode).to.equal(204);
expect(response2.statusCode).to.equal(404);
});
});
});