Skip to content

Commit

Permalink
Added function for safe cloning user object
Browse files Browse the repository at this point in the history
  • Loading branch information
chrisbujok committed Dec 21, 2016
1 parent 70852ce commit bb08bef
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 2 deletions.
32 changes: 30 additions & 2 deletions src/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ const ensureValuesAreStrings = (...rest) => {
};

const sanitizeUserForClient = user => {
const user1 = Object.assign({}, user);
const user1 = cloneUserObject(user);

delete user1.password;
delete user1.verifyExpires;
Expand All @@ -140,11 +140,39 @@ const sanitizeUserForClient = user => {
};

const sanitizeUserForNotifier = user => {
const user1 = Object.assign({}, user);
const user1 = cloneUserObject(user);
delete user1.password;
return user1;
};

/**
* Returns new object with values cloned from original user object.
* Some objects (like Sequelize model instances) contain circular references
* and cause TypeError when trying to JSON.stringify() them. They may contain
* custom toJSON() method which allows to serialize them safely.
* Object.assign() does not clone original toJSON(), so the purpose of this method
* is to use result of custom toJSON() (if accessible) for Object.assign(),
* but only in case of serialization failure.
*
* @param {Object?} user - Object to clone
* @returns {Object} Cloned user object
*/
const cloneUserObject = user => {
let user1 = user;

if (typeof user.toJSON === 'function') {
try {
JSON.stringify(Object.assign({}, user1));
} catch (e) {
debug('User object is not serializable');

user1 = user1.toJSON();
}
}

return Object.assign({}, user1);
};

const notifier = (optionsNotifier, type, user, notifierOptions) => {
debug('notifier', type);

Expand Down
56 changes: 56 additions & 0 deletions test/helpers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
const assert = require('chai').assert;
const helpers = require('../src/helpers');

describe('helpers - sanitization', () => {
it('allows to stringify sanitized user object', () => {
const user = {
id: 1,
email: 'test@test.test',
password: '0000000000',
resetToken: 'aaa',
};

const result1 = helpers.sanitizeUserForClient(user);
const result2 = helpers.sanitizeUserForNotifier(user);

assert.doesNotThrow(() => JSON.stringify(result1));
assert.doesNotThrow(() => JSON.stringify(result2));
});

it('throws error when stringifying sanitized object with circular reference', () => {
const user = {
id: 1,
email: 'test@test.test',
password: '0000000000',
resetToken: 'aaa'
};

user.self = user;

const result1 = helpers.sanitizeUserForClient(user);
const result2 = helpers.sanitizeUserForNotifier(user);

assert.throws(() => JSON.stringify(result1), TypeError);
assert.throws(() => JSON.stringify(result2), TypeError);
});

it('allows to stringify sanitized object with circular reference and custom toJSON()', () => {
const user = {
id: 1,
email: 'test@test.test',
password: '0000000000',
resetToken: 'aaa',
toJSON: function() {
return Object.assign({}, this, { self: undefined });
}
};

user.self = user;

const result1 = helpers.sanitizeUserForClient(user);
const result2 = helpers.sanitizeUserForNotifier(user);

assert.doesNotThrow(() => JSON.stringify(result1));
assert.doesNotThrow(() => JSON.stringify(result2));
});
});

0 comments on commit bb08bef

Please sign in to comment.