diff --git a/app/api/server/helpers/getUserInfo.js b/app/api/server/helpers/getUserInfo.js index 379c5301d97e..f7098beff333 100644 --- a/app/api/server/helpers/getUserInfo.js +++ b/app/api/server/helpers/getUserInfo.js @@ -1,5 +1,5 @@ import { settings } from '../../../settings'; -import { getUserPreference } from '../../../utils'; +import { getUserPreference, getURL } from '../../../utils'; import { API } from '../api'; const getInfoFromUserObject = (user) => { @@ -54,6 +54,9 @@ API.helperMethods.set('getUserInfo', function _getUserInfo(user) { }; const verifiedEmail = isVerifiedEmail(); me.email = verifiedEmail ? verifiedEmail.address : undefined; + + me.avatarUrl = getURL(`/avatar/${ me.username }`, { cdn: false, full: true }); + me.settings = { preferences: getUserPreferences(), }; diff --git a/app/apps/lib/misc/transformMappedData.js b/app/apps/lib/misc/transformMappedData.js new file mode 100644 index 000000000000..0bba2c646081 --- /dev/null +++ b/app/apps/lib/misc/transformMappedData.js @@ -0,0 +1,85 @@ +import cloneDeep from 'lodash.clonedeep'; + +/** + * Transforms a `data` source object to another object, + * essentially applying a to -> from mapping provided by + * `map`. It does not mutate the `data` object. + * + * It also inserts in the `transformedObject` a new property + * called `_unmappedProperties_` which contains properties from + * the original `data` that have not been mapped to its transformed + * counterpart. E.g.: + * + * ```javascript + * const data = { _id: 'abcde123456', size: 10 }; + * const map = { id: '_id' } + * + * transformMappedData(data, map); + * // { id: 'abcde123456', _unmappedProperties_: { size: 10 } } + * ``` + * + * In order to compute the unmapped properties, this function will + * ignore any property on `data` that has been named on the "from" part + * of the `map`, and will consider properties not mentioned as unmapped. + * + * You can also define the "from" part as a function, so you can derive a + * new value for your property from the original `data`. This function will + * receive a copy of the original `data` for it to calculate the value + * for its "to" field. Please note that in this case `transformMappedData` + * will not be able to determine the source field from your map, so it won't + * ignore any field you've used to derive your new value. For that, you're + * going to need to delete the value from the received parameter. E.g: + * + * ```javascript + * const data = { _id: 'abcde123456', size: 10 }; + * + * // It will look like the `size` property is not mapped + * const map = { + * id: '_id', + * newSize: (data) => data.size + 10 + * }; + * + * transformMappedData(data, map); + * // { id: 'abcde123456', newSize: 20, _unmappedProperties_: { size: 10 } } + * + * // You need to explicitly remove it from the original `data` + * const map = { + * id: '_id', + * newSize: (data) => { + * const result = data.size + 10; + * delete data.size; + * return result; + * } + * }; + * + * transformMappedData(data, map); + * // { id: 'abcde123456', newSize: 20, _unmappedProperties_: {} } + * ``` + * + * @param Object data The data to be transformed + * @param Object map The map with transformations to be applied + * + * @returns Object The data after transformations have been applied + */ + +export const transformMappedData = (data, map) => { + const originalData = cloneDeep(data); + const transformedData = {}; + + Object.entries(map).forEach(([to, from]) => { + if (typeof from === 'function') { + const result = from(originalData); + + if (typeof result !== 'undefined') { + transformedData[to] = result; + } + } else if (typeof from === 'string' && typeof originalData[from] !== 'undefined') { + transformedData[to] = originalData[from]; + delete originalData[from]; + } + }); + + transformedData._unmappedProperties_ = originalData; + + return transformedData; +}; diff --git a/app/apps/server/bridges/http.js b/app/apps/server/bridges/http.js index 2082078f0d11..743343fa12c9 100644 --- a/app/apps/server/bridges/http.js +++ b/app/apps/server/bridges/http.js @@ -6,10 +6,6 @@ export class AppHttpBridge { } async call(info) { - if (typeof info.request.timeout !== 'number' || info.request.timeout > 500) { - info.request.timeout = 500; - } - if (!info.request.content && typeof info.request.data === 'object') { info.request.content = JSON.stringify(info.request.data); } diff --git a/app/apps/server/converters/messages.js b/app/apps/server/converters/messages.js index ef93fc37fa1f..909af3171235 100644 --- a/app/apps/server/converters/messages.js +++ b/app/apps/server/converters/messages.js @@ -1,5 +1,6 @@ import { Random } from 'meteor/random'; import { Messages, Rooms, Users } from '../../../models'; +import { transformMappedData } from '../../lib/misc/transformMappedData'; export class AppMessagesConverter { constructor(orch) { @@ -17,42 +18,55 @@ export class AppMessagesConverter { return undefined; } - const room = this.orch.getConverters().get('rooms').convertById(msgObj.rid); + const map = { + id: '_id', + reactions: 'reactions', + parseUrls: 'parseUrls', + text: 'msg', + createdAt: 'ts', + updatedAt: '_updatedAt', + editedAt: 'editedAt', + emoji: 'emoji', + avatarUrl: 'avatar', + alias: 'alias', + customFields: 'customFields', + groupable: 'groupable', + room: (message) => { + const result = this.orch.getConverters().get('rooms').convertById(message.rid); + delete message.rid; + return result; + }, + editor: (message) => { + const { editedBy } = message; + delete message.editedBy; + + if (!editedBy) { + return undefined; + } - let sender; - if (msgObj.u && msgObj.u._id) { - sender = this.orch.getConverters().get('users').convertById(msgObj.u._id); + return this.orch.getConverters().get('users').convertById(editedBy._id); + }, + attachments: (message) => { + const result = this._convertAttachmentsToApp(message.attachments); + delete message.attachments; + return result; + }, + sender: (message) => { + let result; + + if (message.u && message.u._id) { + result = this.orch.getConverters().get('users').convertById(message.u._id); + } else { + result = this.orch.getConverters().get('users').convertToApp(message.u); + } - if (!sender) { - sender = this.orch.getConverters().get('users').convertToApp(msgObj.u); - } - } + delete message.u; - let editor; - if (msgObj.editedBy) { - editor = this.orch.getConverters().get('users').convertById(msgObj.editedBy._id); - } - - const attachments = this._convertAttachmentsToApp(msgObj.attachments); - - return { - id: msgObj._id, - room, - sender, - text: msgObj.msg, - createdAt: msgObj.ts, - updatedAt: msgObj._updatedAt, - editor, - editedAt: msgObj.editedAt, - emoji: msgObj.emoji, - avatarUrl: msgObj.avatar, - alias: msgObj.alias, - customFields: msgObj.customFields, - groupable: msgObj.groupable, - attachments, - reactions: msgObj.reactions, - parseUrls: msgObj.parseUrls, + return result; + }, }; + + return transformMappedData(msgObj, map); } convertAppMessage(message) { @@ -96,7 +110,7 @@ export class AppMessagesConverter { const attachments = this._convertAppAttachments(message.attachments); - return { + const newMessage = { _id: message.id || Random.id(), rid: room._id, u, @@ -114,6 +128,8 @@ export class AppMessagesConverter { reactions: message.reactions, parseUrls: message.parseUrls, }; + + return Object.assign(newMessage, message._unmappedProperties_); } _convertAppAttachments(attachments) { @@ -121,7 +137,7 @@ export class AppMessagesConverter { return undefined; } - return attachments.map((attachment) => ({ + return attachments.map((attachment) => Object.assign({ collapsed: attachment.collapsed, color: attachment.color, text: attachment.text, @@ -150,15 +166,7 @@ export class AppMessagesConverter { actions: attachment.actions, type: attachment.type, description: attachment.description, - })).map((a) => { - Object.keys(a).forEach((k) => { - if (typeof a[k] === 'undefined') { - delete a[k]; - } - }); - - return a; - }); + }, attachment._unmappedProperties_)); } _convertAttachmentsToApp(attachments) { @@ -166,51 +174,61 @@ export class AppMessagesConverter { return undefined; } - return attachments.map((attachment) => { - let author; - if (attachment.author_name || attachment.author_link || attachment.author_icon) { - author = { - name: attachment.author_name, - link: attachment.author_link, - icon: attachment.author_icon, - }; - } - - let title; - if (attachment.title || attachment.title_link || attachment.title_link_download) { - title = { - value: attachment.title, - link: attachment.title_link, - displayDownloadLink: attachment.title_link_download, - }; - } + const map = { + collapsed: 'collapsed', + color: 'color', + text: 'text', + timestampLink: 'message_link', + thumbnailUrl: 'thumb_url', + imageDimensions: 'image_dimensions', + imagePreview: 'image_preview', + imageUrl: 'image_url', + imageType: 'image_type', + imageSize: 'image_size', + audioUrl: 'audio_url', + audioType: 'audio_type', + audioSize: 'audio_size', + videoUrl: 'video_url', + videoType: 'video_type', + videoSize: 'video_size', + fields: 'fields', + actionButtonsAlignment: 'button_alignment', + actions: 'actions', + type: 'type', + description: 'description', + author: (attachment) => { + const { + author_name: name, + author_link: link, + author_icon: icon, + } = attachment; + + delete attachment.author_name; + delete attachment.author_link; + delete attachment.author_icon; + + return { name, link, icon }; + }, + title: (attachment) => { + const { + title: value, + title_link: link, + title_link_download: displayDownloadLink, + } = attachment; + + delete attachment.title; + delete attachment.title_link; + delete attachment.title_link_download; + + return { value, link, displayDownloadLink }; + }, + timestamp: (attachment) => { + const result = new Date(attachment.ts); + delete attachment.ts; + return result; + }, + }; - return { - collapsed: attachment.collapsed, - color: attachment.color, - text: attachment.text, - timestamp: new Date(attachment.ts), - timestampLink: attachment.message_link, - thumbnailUrl: attachment.thumb_url, - author, - title, - imageDimensions: attachment.image_dimensions, - imagePreview: attachment.image_preview, - imageUrl: attachment.image_url, - imageType: attachment.image_type, - imageSize: attachment.image_size, - audioUrl: attachment.audio_url, - audioType: attachment.audio_type, - audioSize: attachment.audio_size, - videoUrl: attachment.video_url, - videoType: attachment.video_type, - videoSize: attachment.video_size, - fields: attachment.fields, - actionButtonsAlignment: attachment.button_alignment, - actions: attachment.actions, - type: attachment.type, - description: attachment.description, - }; - }); + return attachments.map((attachment) => transformMappedData(attachment, map)); } } diff --git a/app/apps/server/tests/messages.tests.js b/app/apps/server/tests/messages.tests.js new file mode 100644 index 000000000000..f057b6fcb331 --- /dev/null +++ b/app/apps/server/tests/messages.tests.js @@ -0,0 +1,159 @@ +/* eslint-env mocha */ +import 'babel-polyfill'; +import mock from 'mock-require'; +import chai from 'chai'; + +import { AppServerOrchestratorMock } from './mocks/orchestrator.mock'; +import { appMessageMock, rocketchatMessageMock, appMessageInvalidRoomMock } from './mocks/data/messages.data'; + +chai.use(require('chai-datetime')); +const { expect } = chai; + +mock('../../../models', './mocks/models'); +mock('meteor/random', { + id: () => 1, +}); + +const { AppMessagesConverter } = require('../converters/messages'); + +const userMock = { + username: 'rocket.cat', + emails: [{ + address: 'rocketcat@rocket.chat', + verified: true, + }], + type: 'bot', + isEnabled: true, + name: 'Rocket.Cat', + roles: ['bot'], + status: 'online', + statusConnection: 'online', + utcOffset: 0, + createdAt: new Date(), + updatedAt: new Date(), + lastLoginAt: undefined, +}; + +const roomMock = { + displayName: 'Mocked Room', + slugifiedName: 'mocked-room', + type: 'c', + creator: userMock, +}; + +describe('The AppMessagesConverter instance', function() { + let messagesConverter; + + before(function() { + const orchestrator = new AppServerOrchestratorMock(); + + orchestrator.getConverters().get('users').convertById = function convertUserByIdStub(id) { + return { + id, + ...userMock, + }; + }; + + orchestrator.getConverters().get('rooms').convertById = function convertRoomByIdStub(id) { + return { + id, + ...roomMock, + }; + }; + + messagesConverter = new AppMessagesConverter(orchestrator); + }); + + const createdAt = new Date('2019-03-30T01:22:08.389Z'); + const updatedAt = new Date('2019-03-30T01:22:08.412Z'); + + describe('when converting a message from Rocket.Chat to the Engine schema', function() { + + it('should return `undefined` when `msgObj` is falsy', function() { + const appMessage = messagesConverter.convertMessage(undefined); + + expect(appMessage).to.be.undefined; + }); + + it('should return a proper schema', function() { + const appMessage = messagesConverter.convertMessage(rocketchatMessageMock); + + expect(appMessage).to.have.property('id', 'bojapwB2udErwrvCZ'); + expect(appMessage).to.have.property('createdAt').which.equalTime(createdAt); + expect(appMessage).to.have.property('updatedAt').which.equalTime(updatedAt); + expect(appMessage).to.have.property('groupable', false); + expect(appMessage).to.have.property('sender').which.includes({ id: 'rocket.cat' }); + expect(appMessage).to.have.property('room').which.includes({ id: 'GENERAL' }); + + expect(appMessage).not.to.have.property('editor'); + expect(appMessage).not.to.have.property('attachments'); + expect(appMessage).not.to.have.property('reactions'); + expect(appMessage).not.to.have.property('avatarUrl'); + expect(appMessage).not.to.have.property('alias'); + expect(appMessage).not.to.have.property('customFields'); + expect(appMessage).not.to.have.property('emoji'); + }); + + it('should not mutate the original message object', function() { + messagesConverter.convertMessage(rocketchatMessageMock); + + expect(rocketchatMessageMock).to.deep.equal({ + _id : 'bojapwB2udErwrvCZ', + t : 'uj', + rid : 'GENERAL', + ts : createdAt, + msg : 'rocket.cat', + u : { + _id : 'rocket.cat', + username : 'rocket.cat', + }, + groupable : false, + _updatedAt : updatedAt, + }); + }); + + it('should add an `_unmappedProperties_` field to the converted message which contains the `t` property of the message', function() { + const appMessage = messagesConverter.convertMessage(rocketchatMessageMock); + + expect(appMessage) + .to.have.property('_unmappedProperties_') + .which.has.property('t') + .which.equal('uj'); + }); + }); + + describe('when converting a message from the Engine schema back to Rocket.Chat', function() { + + it('should return `undefined` when `message` is falsy', function() { + const rocketchatMessage = messagesConverter.convertAppMessage(undefined); + + expect(rocketchatMessage).to.be.undefined; + }); + + it('should return a proper schema', function() { + const rocketchatMessage = messagesConverter.convertAppMessage(appMessageMock); + + expect(rocketchatMessage).to.have.property('_id', 'bojapwB2udErwrvCZ'); + expect(rocketchatMessage).to.have.property('rid', 'GENERAL'); + expect(rocketchatMessage).to.have.property('groupable', false); + expect(rocketchatMessage).to.have.property('ts').which.equalTime(createdAt); + expect(rocketchatMessage).to.have.property('_updatedAt').which.equalTime(updatedAt); + expect(rocketchatMessage).to.have.property('u').which.includes({ + _id: 'rocket.cat', + username: 'rocket.cat', + name: 'Rocket.Cat', + }); + }); + + it('should merge `_unmappedProperties_` into the returned message', function() { + const rocketchatMessage = messagesConverter.convertAppMessage(appMessageMock); + + expect(rocketchatMessage).not.to.have.property('_unmappedProperties_'); + expect(rocketchatMessage).to.have.property('t', 'uj'); + }); + + it('should throw if message has an invalid room', function() { + expect(() => messagesConverter.convertAppMessage(appMessageInvalidRoomMock)).to.throw; + }); + }); +}); diff --git a/app/apps/server/tests/mocks/data/messages.data.js b/app/apps/server/tests/mocks/data/messages.data.js new file mode 100644 index 000000000000..c57ccf822551 --- /dev/null +++ b/app/apps/server/tests/mocks/data/messages.data.js @@ -0,0 +1,129 @@ +export const appMessageMock = { + id: 'bojapwB2udErwrvCZ', + text: 'rocket.cat', + createdAt: new Date('2019-03-30T01:22:08.389Z'), + updatedAt: new Date('2019-03-30T01:22:08.412Z'), + groupable: false, + room: { + id: 'GENERAL', + displayName: 'Mocked Room', + slugifiedName: 'mocked-room', + type: 'c', + creator: { + username: 'rocket.cat', + emails: [ + { + address: 'rocketcat@rocket.chat', + verified: true, + }, + ], + type: 'bot', + isEnabled: true, + name: 'Rocket.Cat', + roles: [ + 'bot', + ], + status: 'online', + statusConnection: 'online', + utcOffset: 0, + createdAt: new Date('2019-04-13T01:33:14.191Z'), + updatedAt: new Date('2019-04-13T01:33:14.191Z'), + }, + }, + sender: { + id: 'rocket.cat', + username: 'rocket.cat', + emails: [ + { + address: 'rocketcat@rocket.chat', + verified: true, + }, + ], + type: 'bot', + isEnabled: true, + name: 'Rocket.Cat', + roles: [ + 'bot', + ], + status: 'online', + statusConnection: 'online', + utcOffset: 0, + createdAt: new Date('2019-04-13T01:33:14.191Z'), + updatedAt: new Date('2019-04-13T01:33:14.191Z'), + }, + _unmappedProperties_: { + t: 'uj', + }, +}; + +export const appMessageInvalidRoomMock = { + id: 'bojapwB2udErwrvCZ', + text: 'rocket.cat', + createdAt: new Date('2019-03-30T01:22:08.389Z'), + updatedAt: new Date('2019-03-30T01:22:08.412Z'), + groupable: false, + room: { + id: 'INVALID IDENTIFICATION', + displayName: 'Mocked Room', + slugifiedName: 'mocked-room', + type: 'c', + creator: { + username: 'rocket.cat', + emails: [ + { + address: 'rocketcat@rocket.chat', + verified: true, + }, + ], + type: 'bot', + isEnabled: true, + name: 'Rocket.Cat', + roles: [ + 'bot', + ], + status: 'online', + statusConnection: 'online', + utcOffset: 0, + createdAt: new Date('2019-04-13T01:33:14.191Z'), + updatedAt: new Date('2019-04-13T01:33:14.191Z'), + }, + }, + sender: { + id: 'rocket.cat', + username: 'rocket.cat', + emails: [ + { + address: 'rocketcat@rocket.chat', + verified: true, + }, + ], + type: 'bot', + isEnabled: true, + name: 'Rocket.Cat', + roles: [ + 'bot', + ], + status: 'online', + statusConnection: 'online', + utcOffset: 0, + createdAt: new Date('2019-04-13T01:33:14.191Z'), + updatedAt: new Date('2019-04-13T01:33:14.191Z'), + }, + _unmappedProperties_: { + t: 'uj', + }, +}; + +export const rocketchatMessageMock = { + _id : 'bojapwB2udErwrvCZ', + t : 'uj', + rid : 'GENERAL', + ts : new Date('2019-03-30T01:22:08.389Z'), + msg : 'rocket.cat', + u : { + _id : 'rocket.cat', + username : 'rocket.cat', + }, + groupable : false, + _updatedAt : new Date('2019-03-30T01:22:08.412Z'), +}; diff --git a/app/apps/server/tests/mocks/models/BaseModel.mock.js b/app/apps/server/tests/mocks/models/BaseModel.mock.js new file mode 100644 index 000000000000..920db0d95fdc --- /dev/null +++ b/app/apps/server/tests/mocks/models/BaseModel.mock.js @@ -0,0 +1,5 @@ +export class BaseModelMock { + findOneById(id) { + return this.data[id]; + } +} diff --git a/app/apps/server/tests/mocks/models/Messages.mock.js b/app/apps/server/tests/mocks/models/Messages.mock.js new file mode 100644 index 000000000000..b17b55a689ce --- /dev/null +++ b/app/apps/server/tests/mocks/models/Messages.mock.js @@ -0,0 +1,19 @@ +import { BaseModelMock } from './BaseModel.mock'; + +export class MessagesMock extends BaseModelMock { + data = { + 1: { + _id : 1, + t : 'uj', + rid : 'GENERAL', + ts : new Date('2019-03-30T01:22:08.389Z'), + msg : 'rocket.cat', + u : { + _id : 'rocket.cat', + username : 'rocket.cat', + }, + groupable : false, + _updatedAt : new Date('2019-03-30T01:22:08.412Z'), + }, + } +} diff --git a/app/apps/server/tests/mocks/models/Rooms.mock.js b/app/apps/server/tests/mocks/models/Rooms.mock.js new file mode 100644 index 000000000000..0ef277b10594 --- /dev/null +++ b/app/apps/server/tests/mocks/models/Rooms.mock.js @@ -0,0 +1,31 @@ +import { BaseModelMock } from './BaseModel.mock'; + +export class RoomsMock extends BaseModelMock { + data = { + GENERAL: { + _id : 'GENERAL', + ts : new Date('2019-03-27T20:51:36.808Z'), + t : 'c', + name : 'general', + usernames : [], + msgs : 31, + usersCount : 3, + default : true, + _updatedAt : new Date('2019-04-10T17:44:34.931Z'), + lastMessage : { + _id : 1, + t : 'uj', + rid : 'GENERAL', + ts : new Date('2019-03-30T01:22:08.389Z'), + msg : 'rocket.cat', + u : { + _id : 'rocket.cat', + username : 'rocket.cat', + }, + groupable : false, + _updatedAt : new Date('2019-03-30T01:22:08.412Z'), + }, + lm : new Date('2019-04-10T17:44:34.873Z'), + }, + } +} diff --git a/app/apps/server/tests/mocks/models/Users.mock.js b/app/apps/server/tests/mocks/models/Users.mock.js new file mode 100644 index 000000000000..1b15b72c664a --- /dev/null +++ b/app/apps/server/tests/mocks/models/Users.mock.js @@ -0,0 +1,22 @@ +import { BaseModelMock } from './BaseModel.mock'; + +export class UsersMock extends BaseModelMock { + data = { + 'rocket.cat': { + _id : 'rocket.cat', + createdAt : new Date('2019-03-27T20:51:36.821Z'), + avatarOrigin : 'local', + name : 'Rocket.Cat', + username : 'rocket.cat', + status : 'online', + statusDefault : 'online', + utcOffset : 0, + active : true, + type : 'bot', + _updatedAt : new Date('2019-03-30T01:11:50.496Z'), + roles : [ + 'bot', + ], + }, + } +} diff --git a/app/apps/server/tests/mocks/models/index.js b/app/apps/server/tests/mocks/models/index.js new file mode 100644 index 000000000000..725705014607 --- /dev/null +++ b/app/apps/server/tests/mocks/models/index.js @@ -0,0 +1,7 @@ +import { MessagesMock } from './Messages.mock'; +import { RoomsMock } from './Rooms.mock'; +import { UsersMock } from './Users.mock'; + +export const Messages = new MessagesMock(); +export const Rooms = new RoomsMock(); +export const Users = new UsersMock(); diff --git a/app/apps/server/tests/mocks/orchestrator.mock.js b/app/apps/server/tests/mocks/orchestrator.mock.js new file mode 100644 index 000000000000..a0ec3410f025 --- /dev/null +++ b/app/apps/server/tests/mocks/orchestrator.mock.js @@ -0,0 +1,106 @@ +export class AppServerOrchestratorMock { + constructor() { + + this._marketplaceUrl = 'https://marketplace.rocket.chat'; + + this._model = {}; + this._logModel = {}; + this._persistModel = {}; + this._storage = {}; + this._logStorage = {}; + + this._converters = new Map(); + this._converters.set('messages', {}); + this._converters.set('rooms', {}); + this._converters.set('settings', {}); + this._converters.set('users', {}); + + this._bridges = {}; + + this._manager = {}; + + this._communicators = new Map(); + this._communicators.set('methods', {}); + this._communicators.set('notifier', {}); + this._communicators.set('restapi', {}); + } + + getModel() { + return this._model; + } + + getPersistenceModel() { + return this._persistModel; + } + + getStorage() { + return this._storage; + } + + getLogStorage() { + return this._logStorage; + } + + getConverters() { + return this._converters; + } + + getBridges() { + return this._bridges; + } + + getNotifier() { + return this._communicators.get('notifier'); + } + + getManager() { + return this._manager; + } + + isEnabled() { + return true; + } + + isLoaded() { + return this.getManager().areAppsLoaded(); + } + + isDebugging() { + return true; + } + + debugLog() { + if (this.isDebugging()) { + // eslint-disable-next-line + console.log(...arguments); + } + } + + getMarketplaceUrl() { + return this._marketplaceUrl; + } + + load() { + // Don't try to load it again if it has + // already been loaded + if (this.isLoaded()) { + return; + } + + this._manager.load() + .then((affs) => console.log(`Loaded the Apps Framework and loaded a total of ${ affs.length } Apps!`)) + .catch((err) => console.warn('Failed to load the Apps Framework and Apps!', err)); + } + + unload() { + // Don't try to unload it if it's already been + // unlaoded or wasn't unloaded to start with + if (!this.isLoaded()) { + return; + } + + this._manager.unload() + .then(() => console.log('Unloaded the Apps Framework.')) + .catch((err) => console.warn('Failed to unload the Apps Framework!', err)); + } +} diff --git a/app/custom-oauth/server/custom_oauth_server.js b/app/custom-oauth/server/custom_oauth_server.js index 2195b4d433fb..ff32b7aeced7 100644 --- a/app/custom-oauth/server/custom_oauth_server.js +++ b/app/custom-oauth/server/custom_oauth_server.js @@ -72,6 +72,7 @@ export class CustomOAuth { this.tokenSentVia = options.tokenSentVia; this.identityTokenSentVia = options.identityTokenSentVia; this.usernameField = (options.usernameField || '').trim(); + this.avatarField = (options.avatarField || '').trim(); this.mergeUsers = options.mergeUsers; this.mergeRoles = options.mergeRoles || false; this.rolesClaim = options.rolesClaim || 'roles'; @@ -144,7 +145,7 @@ export class CustomOAuth { } } - getIdentity(accessToken, accessTokenParam) { + getIdentity(accessToken) { const params = {}; const headers = { 'User-Agent': this.userAgent, // http://doc.gitlab.com/ce/api/users.html#Current-user @@ -153,7 +154,7 @@ export class CustomOAuth { if (this.identityTokenSentVia === 'header') { headers.Authorization = `Bearer ${ accessToken }`; } else { - params[accessTokenParam] = accessToken; + params[this.accessTokenParam] = accessToken; } try { @@ -184,8 +185,7 @@ export class CustomOAuth { OAuth.registerService(this.name, 2, null, (query) => { const response = self.getAccessToken(query); - - const identity = self.getIdentity(response.access_token, this.accessTokenParam); + const identity = self.getIdentity(accessToken); const serviceData = { _OAuthCustom: true, @@ -251,6 +251,7 @@ export class CustomOAuth { } identity.email = identity.user.email; } + // Fix for Xenforo [BD]API plugin for 'user.user_id; instead of 'id' if (identity.user && identity.user.user_id && !identity.id) { identity.id = identity.user.user_id; @@ -294,6 +295,10 @@ export class CustomOAuth { identity.username = this.getUsername(identity); } + if (this.avatarField) { + identity.avatarUrl = this.getAvatarUrl(identity); + } + identity.name = this.getName(identity); return identity; @@ -309,12 +314,25 @@ export class CustomOAuth { username = this.usernameField.split('.').reduce(function(prev, curr) { return prev ? prev[curr] : undefined; }, data); + if (!username) { throw new Meteor.Error('field_not_found', `Username field "${ this.usernameField }" not found in data`, data); } return username; } + getAvatarUrl(data) { + const avatarUrl = this.avatarField.split('.').reduce(function(prev, curr) { + return prev ? prev[curr] : undefined; + }, data); + + if (!avatarUrl) { + logger.debug(`Avatar field "${ this.avatarField }" not found in data`, data); + } + + return avatarUrl; + } + getName(identity) { const name = identity.name || identity.username || identity.nickname || identity.CharacterName || identity.userName || identity.preferred_username || (identity.user && identity.user.name); return name; @@ -375,7 +393,7 @@ export class CustomOAuth { } - registerAccessTokenService(name, accessTokenParam) { + registerAccessTokenService(name) { const self = this; const whitelisted = [ 'id', @@ -392,7 +410,7 @@ export class CustomOAuth { identity: Match.Maybe(Object), })); - const identity = options.identity || self.getIdentity(options.accessToken, accessTokenParam); + const identity = options.identity || self.getIdentity(options.accessToken); const serviceData = { accessToken: options.accessToken, diff --git a/app/lib/server/functions/getAvatarSuggestionForUser.js b/app/lib/server/functions/getAvatarSuggestionForUser.js index dd9cbaa4ab4a..84eec85cf47c 100644 --- a/app/lib/server/functions/getAvatarSuggestionForUser.js +++ b/app/lib/server/functions/getAvatarSuggestionForUser.js @@ -1,6 +1,7 @@ import { check } from 'meteor/check'; import { HTTP } from 'meteor/http'; import { Gravatar } from 'meteor/jparker:gravatar'; +import { ServiceConfiguration } from 'meteor/service-configuration'; import { settings } from '../../../settings'; export function getAvatarSuggestionForUser(user) { @@ -57,6 +58,21 @@ export function getAvatarSuggestionForUser(user) { }); } + for (const service in user.services) { + if (user.services[service]._OAuthCustom) { + const services = ServiceConfiguration.configurations.find({ service }, { fields: { secret: 0 } }).fetch(); + + if (services.length > 0) { + if (user.services[service].avatarUrl) { + avatars.push({ + service, + url: user.services[service].avatarUrl, + }); + } + } + } + } + if (user.emails && user.emails.length > 0) { for (const email of user.emails) { if (email.verified === true) { diff --git a/app/lib/server/methods/addOAuthService.js b/app/lib/server/methods/addOAuthService.js index 0e508c747d0a..e86ad045d30b 100644 --- a/app/lib/server/methods/addOAuthService.js +++ b/app/lib/server/methods/addOAuthService.js @@ -37,7 +37,8 @@ Meteor.methods({ settings.add(`Accounts_OAuth_Custom-${ name }-button_label_color` , '#FFFFFF' , { type: 'string' , group: 'OAuth', section: `Custom OAuth: ${ name }`, i18nLabel: 'Accounts_OAuth_Custom_Button_Label_Color', persistent: true }); settings.add(`Accounts_OAuth_Custom-${ name }-button_color` , '#1d74f5' , { type: 'string' , group: 'OAuth', section: `Custom OAuth: ${ name }`, i18nLabel: 'Accounts_OAuth_Custom_Button_Color', persistent: true }); settings.add(`Accounts_OAuth_Custom-${ name }-username_field` , '' , { type: 'string' , group: 'OAuth', section: `Custom OAuth: ${ name }`, i18nLabel: 'Accounts_OAuth_Custom_Username_Field', persistent: true }); + settings.add(`Accounts_OAuth_Custom-${ name }-avatar_field` , '' , { type: 'string' , group: 'OAuth', section: `Custom OAuth: ${ name }`, i18nLabel: 'Accounts_OAuth_Custom_Avatar_Field', persistent: true }); settings.add(`Accounts_OAuth_Custom-${ name }-roles_claim` , 'roles' , { type: 'string' , group: 'OAuth', section: `Custom OAuth: ${ name }`, i18nLabel: 'Accounts_OAuth_Custom_Roles_Claim', persistent: true }); - settings.add(`Accounts_OAuth_Custom-${ name }-merge_roles` , false , { type: 'boolean', group: 'OAuth', section: `Custom OAuth: ${ name }`, i18nLabel: 'Accounts_OAuth_Custom_Merge_Roles', persistent: true }); + settings.add(`Accounts_OAuth_Custom-${ name }-merge_roles` , false , { type: 'boolean', group: 'OAuth', section: `Custom OAuth: ${ name }`, i18nLabel: 'Accounts_OAuth_Custom_Merge_Roles', persistent: true }); settings.add(`Accounts_OAuth_Custom-${ name }-merge_users` , false , { type: 'boolean', group: 'OAuth', section: `Custom OAuth: ${ name }`, i18nLabel: 'Accounts_OAuth_Custom_Merge_Users', persistent: true }); } }); diff --git a/app/lib/server/methods/removeOAuthService.js b/app/lib/server/methods/removeOAuthService.js index 16f61ed3b89c..f4ab2eaf5eec 100644 --- a/app/lib/server/methods/removeOAuthService.js +++ b/app/lib/server/methods/removeOAuthService.js @@ -35,6 +35,7 @@ Meteor.methods({ settings.removeById(`Accounts_OAuth_Custom-${ name }-button_color`); settings.removeById(`Accounts_OAuth_Custom-${ name }-login_style`); settings.removeById(`Accounts_OAuth_Custom-${ name }-username_field`); + settings.removeById(`Accounts_OAuth_Custom-${ name }-avatar_field`); settings.removeById(`Accounts_OAuth_Custom-${ name }-roles_claim`); settings.removeById(`Accounts_OAuth_Custom-${ name }-merge_roles`); settings.removeById(`Accounts_OAuth_Custom-${ name }-merge_users`); diff --git a/app/lib/server/methods/sendMessage.js b/app/lib/server/methods/sendMessage.js index 0fd071583281..cbc50c642e75 100644 --- a/app/lib/server/methods/sendMessage.js +++ b/app/lib/server/methods/sendMessage.js @@ -14,6 +14,7 @@ import { Users, Messages } from '../../../models'; import { sendMessage } from '../functions'; import { RateLimiter } from '../lib'; import { canSendMessage } from '../../../authorization/server'; +import { SystemLogger } from '../../../logger/server'; Meteor.methods({ sendMessage(message) { @@ -90,6 +91,8 @@ Meteor.methods({ throw new Meteor.Error('error-not-allowed'); } + SystemLogger.error('Error sending message:', error); + Notifications.notifyUser(uid, 'message', { _id: Random.id(), rid: message.rid, diff --git a/app/lib/server/startup/oAuthServicesUpdate.js b/app/lib/server/startup/oAuthServicesUpdate.js index 1a459f955fe0..3e4081386089 100644 --- a/app/lib/server/startup/oAuthServicesUpdate.js +++ b/app/lib/server/startup/oAuthServicesUpdate.js @@ -46,6 +46,7 @@ function _OAuthServicesUpdate() { data.tokenSentVia = settings.get(`${ service.key }-token_sent_via`); data.identityTokenSentVia = settings.get(`${ service.key }-identity_token_sent_via`); data.usernameField = settings.get(`${ service.key }-username_field`); + data.avatarField = settings.get(`${ service.key }-avatar_field`); data.rolesClaim = settings.get(`${ service.key }-roles_claim`); data.mergeUsers = settings.get(`${ service.key }-merge_users`); data.mergeRoles = settings.get(`${ service.key }-merge_roles`); @@ -59,6 +60,7 @@ function _OAuthServicesUpdate() { tokenSentVia: data.tokenSentVia, identityTokenSentVia: data.identityTokenSentVia, usernameField: data.usernameField, + avatarField: data.avatarField, rolesClaim: data.rolesClaim, mergeUsers: data.mergeUsers, mergeRoles: data.mergeRoles, diff --git a/package-lock.json b/package-lock.json index 92928ae5576f..1dcf04d4efac 100644 --- a/package-lock.json +++ b/package-lock.json @@ -674,19 +674,6 @@ "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", "integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA=" }, - "@rocket.chat/apps-engine": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@rocket.chat/apps-engine/-/apps-engine-1.4.0.tgz", - "integrity": "sha512-zBkNvQlEqfmoMjU9zYGktq2S2MzrBOk7EPY6QjU1CydQdV2ZxgpYlSRryJMb2U+ugSwrkYOnqNw2aQ5brtUsaA==", - "requires": { - "adm-zip": "^0.4.9", - "lodash.clonedeep": "^4.5.0", - "semver": "^5.5.0", - "stack-trace": "0.0.10", - "typescript": "^2.9.2", - "uuid": "^3.2.1" - } - }, "@rocket.chat/eslint-config": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/@rocket.chat/eslint-config/-/eslint-config-0.2.0.tgz", @@ -758,7 +745,7 @@ }, "@types/events": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@types/events/-/events-1.2.0.tgz", + "resolved": "http://registry.npmjs.org/@types/events/-/events-1.2.0.tgz", "integrity": "sha512-KEIlhXnIutzKwRbQkGWb/I4HFqBuUykAdHgDED6xqwXJfONCjF5VoE0cXEiurh3XauygxzeDzgtXUqvLkxFzzA==" }, "@types/express": { @@ -1660,7 +1647,7 @@ }, "axios": { "version": "0.18.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.18.0.tgz", + "resolved": "http://registry.npmjs.org/axios/-/axios-0.18.0.tgz", "integrity": "sha1-MtU+SFHv3AoRmTts0AB4nXDAUQI=", "requires": { "follow-redirects": "^1.3.0", @@ -1998,7 +1985,7 @@ }, "babel-plugin-add-module-exports": { "version": "0.2.1", - "resolved": "https://registry.npmjs.org/babel-plugin-add-module-exports/-/babel-plugin-add-module-exports-0.2.1.tgz", + "resolved": "http://registry.npmjs.org/babel-plugin-add-module-exports/-/babel-plugin-add-module-exports-0.2.1.tgz", "integrity": "sha1-mumh9KjcZ/DN7E9K7aHkOl/2XiU=", "dev": true }, @@ -2019,79 +2006,79 @@ }, "babel-plugin-syntax-async-functions": { "version": "6.13.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz", + "resolved": "http://registry.npmjs.org/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz", "integrity": "sha1-ytnK0RkbWtY0vzCuCHI5HgZHvpU=", "dev": true }, "babel-plugin-syntax-async-generators": { "version": "6.13.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-async-generators/-/babel-plugin-syntax-async-generators-6.13.0.tgz", + "resolved": "http://registry.npmjs.org/babel-plugin-syntax-async-generators/-/babel-plugin-syntax-async-generators-6.13.0.tgz", "integrity": "sha1-a8lj67FuzLrmuStZbrfzXDQqi5o=", "dev": true }, "babel-plugin-syntax-class-constructor-call": { "version": "6.18.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-class-constructor-call/-/babel-plugin-syntax-class-constructor-call-6.18.0.tgz", + "resolved": "http://registry.npmjs.org/babel-plugin-syntax-class-constructor-call/-/babel-plugin-syntax-class-constructor-call-6.18.0.tgz", "integrity": "sha1-nLnTn+Q8hgC+yBRkVt3L1OGnZBY=", "dev": true }, "babel-plugin-syntax-class-properties": { "version": "6.13.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-class-properties/-/babel-plugin-syntax-class-properties-6.13.0.tgz", + "resolved": "http://registry.npmjs.org/babel-plugin-syntax-class-properties/-/babel-plugin-syntax-class-properties-6.13.0.tgz", "integrity": "sha1-1+sjt5oxf4VDlixQW4J8fWysJ94=", "dev": true }, "babel-plugin-syntax-decorators": { "version": "6.13.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-decorators/-/babel-plugin-syntax-decorators-6.13.0.tgz", + "resolved": "http://registry.npmjs.org/babel-plugin-syntax-decorators/-/babel-plugin-syntax-decorators-6.13.0.tgz", "integrity": "sha1-MSVjtNvePMgGzuPkFszurd0RrAs=", "dev": true }, "babel-plugin-syntax-do-expressions": { "version": "6.13.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-do-expressions/-/babel-plugin-syntax-do-expressions-6.13.0.tgz", + "resolved": "http://registry.npmjs.org/babel-plugin-syntax-do-expressions/-/babel-plugin-syntax-do-expressions-6.13.0.tgz", "integrity": "sha1-V0d1YTmqJtOQ0JQQsDdEugfkeW0=", "dev": true }, "babel-plugin-syntax-dynamic-import": { "version": "6.18.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-dynamic-import/-/babel-plugin-syntax-dynamic-import-6.18.0.tgz", + "resolved": "http://registry.npmjs.org/babel-plugin-syntax-dynamic-import/-/babel-plugin-syntax-dynamic-import-6.18.0.tgz", "integrity": "sha1-jWomIpyDdFqZgqRBBRVyyqF5sdo=", "dev": true }, "babel-plugin-syntax-exponentiation-operator": { "version": "6.13.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-exponentiation-operator/-/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz", + "resolved": "http://registry.npmjs.org/babel-plugin-syntax-exponentiation-operator/-/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz", "integrity": "sha1-nufoM3KQ2pUoggGmpX9BcDF4MN4=", "dev": true }, "babel-plugin-syntax-export-extensions": { "version": "6.13.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-export-extensions/-/babel-plugin-syntax-export-extensions-6.13.0.tgz", + "resolved": "http://registry.npmjs.org/babel-plugin-syntax-export-extensions/-/babel-plugin-syntax-export-extensions-6.13.0.tgz", "integrity": "sha1-cKFITw+QiaToStRLrDU8lbmxJyE=", "dev": true }, "babel-plugin-syntax-flow": { "version": "6.18.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-flow/-/babel-plugin-syntax-flow-6.18.0.tgz", + "resolved": "http://registry.npmjs.org/babel-plugin-syntax-flow/-/babel-plugin-syntax-flow-6.18.0.tgz", "integrity": "sha1-TDqyCiryaqIM0lmVw5jE63AxDI0=", "dev": true }, "babel-plugin-syntax-function-bind": { "version": "6.13.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-function-bind/-/babel-plugin-syntax-function-bind-6.13.0.tgz", + "resolved": "http://registry.npmjs.org/babel-plugin-syntax-function-bind/-/babel-plugin-syntax-function-bind-6.13.0.tgz", "integrity": "sha1-SMSV8Xe98xqYHnMvVa3AvdJgH0Y=", "dev": true }, "babel-plugin-syntax-jsx": { "version": "6.18.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz", + "resolved": "http://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz", "integrity": "sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY=", "dev": true }, "babel-plugin-syntax-object-rest-spread": { "version": "6.13.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz", + "resolved": "http://registry.npmjs.org/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz", "integrity": "sha1-/WU28rzhODb/o6VFjEkDpZe7O/U=", "dev": true }, @@ -2488,7 +2475,7 @@ }, "babel-preset-es2015": { "version": "6.3.13", - "resolved": "https://registry.npmjs.org/babel-preset-es2015/-/babel-preset-es2015-6.3.13.tgz", + "resolved": "http://registry.npmjs.org/babel-preset-es2015/-/babel-preset-es2015-6.3.13.tgz", "integrity": "sha1-l9zn7ykuGMubK3VF2AxZPCjZUX8=", "dev": true, "requires": { @@ -2516,7 +2503,7 @@ }, "babel-preset-react": { "version": "6.3.13", - "resolved": "https://registry.npmjs.org/babel-preset-react/-/babel-preset-react-6.3.13.tgz", + "resolved": "http://registry.npmjs.org/babel-preset-react/-/babel-preset-react-6.3.13.tgz", "integrity": "sha1-E9VeBqZfqqoHw5v2Op2DbgMhFvo=", "dev": true, "requires": { @@ -2530,7 +2517,7 @@ }, "babel-preset-stage-0": { "version": "6.3.13", - "resolved": "https://registry.npmjs.org/babel-preset-stage-0/-/babel-preset-stage-0-6.3.13.tgz", + "resolved": "http://registry.npmjs.org/babel-preset-stage-0/-/babel-preset-stage-0-6.3.13.tgz", "integrity": "sha1-eKN8VvCzmI8qeZMtywzrj/N3sNE=", "dev": true, "requires": { @@ -3425,7 +3412,7 @@ }, "bl": { "version": "1.2.2", - "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.2.tgz", + "resolved": "http://registry.npmjs.org/bl/-/bl-1.2.2.tgz", "integrity": "sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA==", "requires": { "readable-stream": "^2.3.5", @@ -3803,7 +3790,7 @@ }, "readable-stream": { "version": "1.1.14", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", "requires": { "core-util-is": "~1.0.0", @@ -4138,9 +4125,18 @@ "check-error": "^1.0.2" } }, + "chai-datetime": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/chai-datetime/-/chai-datetime-1.5.0.tgz", + "integrity": "sha1-N0LxiwJMdbdqK37uKRZiMkRnWWw=", + "dev": true, + "requires": { + "chai": ">1.9.0" + } + }, "chalk": { "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "requires": { "ansi-styles": "^2.2.1", @@ -4243,7 +4239,7 @@ }, "chimp": { "version": "0.51.1", - "resolved": "https://registry.npmjs.org/chimp/-/chimp-0.51.1.tgz", + "resolved": "http://registry.npmjs.org/chimp/-/chimp-0.51.1.tgz", "integrity": "sha1-6hIbzfJsidV/jvNBlUDPPCeaPMU=", "dev": true, "requires": { @@ -4289,7 +4285,7 @@ "dependencies": { "async": { "version": "0.9.2", - "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz", + "resolved": "http://registry.npmjs.org/async/-/async-0.9.2.tgz", "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=", "dev": true }, @@ -4361,7 +4357,7 @@ }, "minimist": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true }, @@ -4413,7 +4409,7 @@ }, "progress": { "version": "1.1.8", - "resolved": "https://registry.npmjs.org/progress/-/progress-1.1.8.tgz", + "resolved": "http://registry.npmjs.org/progress/-/progress-1.1.8.tgz", "integrity": "sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74=", "dev": true }, @@ -4436,7 +4432,7 @@ }, "chokidar": { "version": "1.6.1", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.6.1.tgz", + "resolved": "http://registry.npmjs.org/chokidar/-/chokidar-1.6.1.tgz", "integrity": "sha1-L0RHq16W5Q+z14n9kNTHLg5McMI=", "dev": true, "requires": { @@ -4688,7 +4684,7 @@ }, "colors": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", + "resolved": "http://registry.npmjs.org/colors/-/colors-1.1.2.tgz", "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=", "dev": true }, @@ -5154,7 +5150,7 @@ "dependencies": { "core-js": { "version": "1.2.7", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz", + "resolved": "http://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz", "integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=" } } @@ -5403,7 +5399,7 @@ "dependencies": { "pify": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", "dev": true } @@ -5433,7 +5429,7 @@ }, "deprecate": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/deprecate/-/deprecate-1.0.0.tgz", + "resolved": "http://registry.npmjs.org/deprecate/-/deprecate-1.0.0.tgz", "integrity": "sha1-ZhSQ7SQokWpsiIPYg05WRvTkpKg=" }, "deprecated-decorator": { @@ -5485,7 +5481,7 @@ }, "readable-stream": { "version": "1.1.14", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", "requires": { "core-util-is": "~1.0.0", @@ -5552,7 +5548,7 @@ "dependencies": { "domelementtype": { "version": "1.1.3", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz", + "resolved": "http://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz", "integrity": "sha1-vSh3PiZCiBrsUVRJJCmcXNgiGFs=" } } @@ -5869,7 +5865,7 @@ }, "es6-promisify": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", + "resolved": "http://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", "requires": { "es6-promise": "^4.0.3" @@ -6280,7 +6276,7 @@ }, "events": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", + "resolved": "http://registry.npmjs.org/events/-/events-1.1.1.tgz", "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=" }, "evp_bytestokey": { @@ -6675,7 +6671,7 @@ }, "external-editor": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz", + "resolved": "http://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz", "integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==", "dev": true, "requires": { @@ -6818,13 +6814,13 @@ "dependencies": { "lodash": { "version": "2.4.2", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz", + "resolved": "http://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz", "integrity": "sha1-+t2DS5aDBz2hebPq5tnA0VBT9z4=", "dev": true }, "underscore.string": { "version": "2.3.3", - "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.3.3.tgz", + "resolved": "http://registry.npmjs.org/underscore.string/-/underscore.string-2.3.3.tgz", "integrity": "sha1-ccCL9rQosRM/N+ePo6Icgvcymw0=", "dev": true } @@ -7751,7 +7747,7 @@ }, "get-stream": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "resolved": "http://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=" }, "get-value": { @@ -7923,7 +7919,7 @@ "dependencies": { "minimist": { "version": "1.1.3", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.1.3.tgz", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.1.3.tgz", "integrity": "sha1-O+39kaktOQFvz6ocaB6Pqhoe/ag=", "dev": true } @@ -8772,7 +8768,7 @@ }, "hapi": { "version": "8.8.0", - "resolved": "https://registry.npmjs.org/hapi/-/hapi-8.8.0.tgz", + "resolved": "http://registry.npmjs.org/hapi/-/hapi-8.8.0.tgz", "integrity": "sha1-h+N6Bum0meiXkOLcERqpZotuYX8=", "dev": true, "requires": { @@ -8842,7 +8838,7 @@ }, "catbox": { "version": "4.3.0", - "resolved": "https://registry.npmjs.org/catbox/-/catbox-4.3.0.tgz", + "resolved": "http://registry.npmjs.org/catbox/-/catbox-4.3.0.tgz", "integrity": "sha1-IiN3vWfxKRrA4l0AAC0GWp3385o=", "dev": true, "requires": { @@ -8939,7 +8935,7 @@ }, "joi": { "version": "6.4.1", - "resolved": "https://registry.npmjs.org/joi/-/joi-6.4.1.tgz", + "resolved": "http://registry.npmjs.org/joi/-/joi-6.4.1.tgz", "integrity": "sha1-9Q9CRTVgBo5jg9oVrC0w3Xzra24=", "dev": true, "requires": { @@ -8951,7 +8947,7 @@ "dependencies": { "isemail": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/isemail/-/isemail-1.1.1.tgz", + "resolved": "http://registry.npmjs.org/isemail/-/isemail-1.1.1.tgz", "integrity": "sha1-4Mj23D9HCX53dzlcaJYnGqJWw7U=", "dev": true }, @@ -8984,7 +8980,7 @@ "dependencies": { "mime-db": { "version": "1.14.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.14.0.tgz", + "resolved": "http://registry.npmjs.org/mime-db/-/mime-db-1.14.0.tgz", "integrity": "sha1-1WHxC27mbbUflK5leilRp0IX7YM=", "dev": true } @@ -9584,7 +9580,7 @@ }, "readable-stream": { "version": "1.1.14", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", "requires": { "core-util-is": "~1.0.0", @@ -9920,7 +9916,7 @@ }, "is-builtin-module": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", + "resolved": "http://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", "dev": true, "requires": { @@ -10075,7 +10071,7 @@ }, "is-obj": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "resolved": "http://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=" }, "is-object": { @@ -10230,7 +10226,7 @@ }, "isemail": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/isemail/-/isemail-1.2.0.tgz", + "resolved": "http://registry.npmjs.org/isemail/-/isemail-1.2.0.tgz", "integrity": "sha1-vgPfjMPineTSxd9lASY/H6RZXpo=" }, "isexe": { @@ -10266,7 +10262,7 @@ }, "jasmine-core": { "version": "2.99.1", - "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-2.99.1.tgz", + "resolved": "http://registry.npmjs.org/jasmine-core/-/jasmine-core-2.99.1.tgz", "integrity": "sha1-5kAN8ea1bhMLYcS80JPap/boyhU=", "dev": true }, @@ -10418,7 +10414,7 @@ }, "jsonfile": { "version": "2.4.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", + "resolved": "http://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=", "dev": true, "requires": { @@ -10701,7 +10697,7 @@ }, "promise": { "version": "6.1.0", - "resolved": "https://registry.npmjs.org/promise/-/promise-6.1.0.tgz", + "resolved": "http://registry.npmjs.org/promise/-/promise-6.1.0.tgz", "integrity": "sha1-LOcp9rlLRcJoka0GAsXJDgTG7vY=", "optional": true, "requires": { @@ -11326,7 +11322,7 @@ }, "media-typer": { "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "resolved": "http://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" }, "mem": { @@ -11630,7 +11626,7 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "requires": { "core-util-is": "~1.0.0", @@ -11652,7 +11648,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "requires": { "safe-buffer": "~5.1.0" @@ -11794,14 +11790,14 @@ "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "requires": { "brace-expansion": "^1.1.7" } }, "minimist": { "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" }, "minimist-options": { @@ -11889,7 +11885,7 @@ }, "mkdirp": { "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "requires": { "minimist": "0.0.8" @@ -12175,7 +12171,7 @@ }, "ncp": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", + "resolved": "http://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", "integrity": "sha1-GVoh1sRuNh0vsSgbo4uR6d9727M=", "optional": true }, @@ -12431,7 +12427,7 @@ }, "npm-install-package": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/npm-install-package/-/npm-install-package-2.1.0.tgz", + "resolved": "http://registry.npmjs.org/npm-install-package/-/npm-install-package-2.1.0.tgz", "integrity": "sha1-1+/jz816sAYUuJbqUxGdyaslkSU=", "dev": true }, @@ -12446,7 +12442,7 @@ "npmlog": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", - "integrity": "sha1-CKfyqL9zRgR3mp76StXMcXq7lUs=", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", "requires": { "are-we-there-yet": "~1.1.2", "console-control-strings": "~1.1.0", @@ -12676,7 +12672,7 @@ }, "os-locale": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "resolved": "http://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", "requires": { "lcid": "^1.0.0" @@ -13003,7 +12999,7 @@ }, "es6-promise": { "version": "4.0.5", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.0.5.tgz", + "resolved": "http://registry.npmjs.org/es6-promise/-/es6-promise-4.0.5.tgz", "integrity": "sha1-eILzCt3lskDM+n99eMVIMwlRrkI=", "dev": true }, @@ -13059,7 +13055,7 @@ }, "progress": { "version": "1.1.8", - "resolved": "https://registry.npmjs.org/progress/-/progress-1.1.8.tgz", + "resolved": "http://registry.npmjs.org/progress/-/progress-1.1.8.tgz", "integrity": "sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74=", "dev": true }, @@ -13107,7 +13103,7 @@ }, "tough-cookie": { "version": "2.3.4", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.4.tgz", + "resolved": "http://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.4.tgz", "integrity": "sha512-TZ6TTfI5NtZnuyy/Kecv+CnoROnyXn2DN97LontgQpCwsX2XyLYCC0ENhYkehSOwAp8rTQKc/NUIF7BkQ5rKLA==", "dev": true, "requires": { @@ -14159,7 +14155,7 @@ "dependencies": { "minimist": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" } } @@ -14175,7 +14171,7 @@ "dependencies": { "pify": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", "dev": true } @@ -14266,7 +14262,7 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "requires": { "core-util-is": "~1.0.0", @@ -14378,7 +14374,7 @@ }, "regjsgen": { "version": "0.2.0", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz", + "resolved": "http://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz", "integrity": "sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc=", "dev": true }, @@ -14541,7 +14537,7 @@ }, "requestretry": { "version": "1.5.0", - "resolved": "https://registry.npmjs.org/requestretry/-/requestretry-1.5.0.tgz", + "resolved": "http://registry.npmjs.org/requestretry/-/requestretry-1.5.0.tgz", "integrity": "sha1-7RV7ulNSbt6z7DKo5wSkmYvs5ic=", "dev": true, "requires": { @@ -14667,7 +14663,7 @@ }, "rimraf": { "version": "2.4.5", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", + "resolved": "http://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", "integrity": "sha1-7nEM5dk6j9uFb7Xqj/Di11k0sto=", "requires": { "glob": "^6.0.1" @@ -14762,7 +14758,7 @@ }, "safe-regex": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "resolved": "http://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", "requires": { "ret": "~0.1.10" @@ -14784,7 +14780,7 @@ }, "sax": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", + "resolved": "http://registry.npmjs.org/sax/-/sax-1.2.1.tgz", "integrity": "sha1-e45lYZCyKOgaZq6nSEgNgozS03o=" }, "schema-inspector": { @@ -14797,7 +14793,7 @@ "dependencies": { "async": { "version": "1.5.2", - "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "resolved": "http://registry.npmjs.org/async/-/async-1.5.2.tgz", "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=" } } @@ -14862,7 +14858,7 @@ }, "minimist": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true }, @@ -15556,7 +15552,7 @@ }, "strip-ansi": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "requires": { "ansi-regex": "^2.0.0" @@ -16105,7 +16101,7 @@ }, "through": { "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "resolved": "http://registry.npmjs.org/through/-/through-2.3.8.tgz", "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", "dev": true }, @@ -16421,11 +16417,6 @@ "resolved": "https://registry.npmjs.org/typeforce/-/typeforce-1.16.0.tgz", "integrity": "sha512-V60F7OHPH7vPlgIU73vYyeebKxWjQqCTlge+MvKlVn09PIhCOi/ZotowYdgREHB5S1dyHOr906ui6NheYXjlVQ==" }, - "typescript": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.9.2.tgz", - "integrity": "sha512-Gr4p6nFNaoufRIY4NMdpQRNmgxVIGMs4Fcu/ujdYk3nAZqk7supzBE9idmvfZIlH/Cuj//dvi+019qEue9lV0w==" - }, "ua-parser-js": { "version": "0.7.19", "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.19.tgz", @@ -16757,7 +16748,7 @@ "dependencies": { "semver": { "version": "5.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", + "resolved": "http://registry.npmjs.org/semver/-/semver-5.3.0.tgz", "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=" } } @@ -17365,7 +17356,7 @@ }, "wrap-ansi": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "resolved": "http://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", "requires": { "string-width": "^1.0.1", @@ -17469,7 +17460,7 @@ }, "xolvio-ddp": { "version": "0.12.3", - "resolved": "https://registry.npmjs.org/xolvio-ddp/-/xolvio-ddp-0.12.3.tgz", + "resolved": "http://registry.npmjs.org/xolvio-ddp/-/xolvio-ddp-0.12.3.tgz", "integrity": "sha1-NqarlhKyQLWg0cCoNJCK8XwLjwI=", "dev": true, "requires": { @@ -17494,7 +17485,7 @@ }, "async": { "version": "0.9.2", - "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz", + "resolved": "http://registry.npmjs.org/async/-/async-0.9.2.tgz", "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=", "dev": true }, @@ -17506,7 +17497,7 @@ }, "bl": { "version": "0.9.5", - "resolved": "https://registry.npmjs.org/bl/-/bl-0.9.5.tgz", + "resolved": "http://registry.npmjs.org/bl/-/bl-0.9.5.tgz", "integrity": "sha1-wGt5evCF6gC8Unr8jvzxHeIjIFQ=", "dev": true, "requires": { @@ -17515,7 +17506,7 @@ }, "bluebird": { "version": "2.11.0", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-2.11.0.tgz", + "resolved": "http://registry.npmjs.org/bluebird/-/bluebird-2.11.0.tgz", "integrity": "sha1-U0uQM8AiyVecVro7Plpcqvu2UOE=", "dev": true }, @@ -17527,7 +17518,7 @@ }, "combined-stream": { "version": "0.0.7", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-0.0.7.tgz", + "resolved": "http://registry.npmjs.org/combined-stream/-/combined-stream-0.0.7.tgz", "integrity": "sha1-ATfmV7qlp1QcV6w3rF/AfXO03B8=", "dev": true, "requires": { @@ -17548,7 +17539,7 @@ }, "form-data": { "version": "0.2.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-0.2.0.tgz", + "resolved": "http://registry.npmjs.org/form-data/-/form-data-0.2.0.tgz", "integrity": "sha1-Jvi8JtpkQOKZy9z7aQNcT3em5GY=", "dev": true, "requires": { @@ -17588,13 +17579,13 @@ }, "mime-db": { "version": "1.12.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.12.0.tgz", + "resolved": "http://registry.npmjs.org/mime-db/-/mime-db-1.12.0.tgz", "integrity": "sha1-PQxjGA9FjrENMlqqN9fFiuMS6dc=", "dev": true }, "mime-types": { "version": "2.0.14", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.0.14.tgz", + "resolved": "http://registry.npmjs.org/mime-types/-/mime-types-2.0.14.tgz", "integrity": "sha1-MQ4VnbI+B3+Lsit0jav6SVcUCqY=", "dev": true, "requires": { @@ -17621,7 +17612,7 @@ }, "readable-stream": { "version": "1.0.34", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", "dev": true, "requires": { @@ -17633,7 +17624,7 @@ }, "request": { "version": "2.53.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.53.0.tgz", + "resolved": "http://registry.npmjs.org/request/-/request-2.53.0.tgz", "integrity": "sha1-GAo66St7Y5gC5PlUXdj83rcddgw=", "dev": true, "requires": { @@ -17672,7 +17663,7 @@ }, "xolvio-fiber-utils": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/xolvio-fiber-utils/-/xolvio-fiber-utils-2.0.3.tgz", + "resolved": "http://registry.npmjs.org/xolvio-fiber-utils/-/xolvio-fiber-utils-2.0.3.tgz", "integrity": "sha1-vsjXDHQGGjFjFbun0w0lyz6C3FA=", "dev": true, "requires": { @@ -17690,7 +17681,7 @@ }, "xolvio-jasmine-expect": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/xolvio-jasmine-expect/-/xolvio-jasmine-expect-1.1.0.tgz", + "resolved": "http://registry.npmjs.org/xolvio-jasmine-expect/-/xolvio-jasmine-expect-1.1.0.tgz", "integrity": "sha1-vCud1ghCMR8EV59agtzqaisxnH0=", "dev": true, "requires": { @@ -17751,7 +17742,7 @@ }, "yargs": { "version": "3.32.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.32.0.tgz", + "resolved": "http://registry.npmjs.org/yargs/-/yargs-3.32.0.tgz", "integrity": "sha1-AwiOnr+edWtpdRYR0qXvWRSCyZU=", "requires": { "camelcase": "^2.0.1", diff --git a/package.json b/package.json index 6e9ab0cee99f..7d3812c51346 100644 --- a/package.json +++ b/package.json @@ -98,6 +98,7 @@ "babel-mocha-es6-compiler": "^0.1.0", "babel-plugin-array-includes": "^2.0.3", "chai": "^3.5.0", + "chai-datetime": "^1.5.0", "chimp": "^0.51.1", "eslint": "^5.9.0", "husky": "^1.2.0", @@ -182,6 +183,7 @@ "less-plugin-autoprefix": "^2.0.0", "limax": "^1.7.0", "localforage": "^1.7.3", + "lodash.clonedeep": "^4.5.0", "lodash.property": "^4.4.2", "lokijs": "^1.5.5", "lru-cache": "^5.1.1", diff --git a/packages/rocketchat-i18n/i18n/en.i18n.json b/packages/rocketchat-i18n/i18n/en.i18n.json index 6813bf10cb33..c5e5eaaf39ab 100644 --- a/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/packages/rocketchat-i18n/i18n/en.i18n.json @@ -78,6 +78,7 @@ "Accounts_LoginExpiration": "Login Expiration in Days", "Accounts_ManuallyApproveNewUsers": "Manually Approve New Users", "Accounts_OAuth_Custom_Authorize_Path": "Authorize Path", + "Accounts_OAuth_Custom_Avatar_Field": "Avatar field", "Accounts_OAuth_Custom_Button_Color": "Button Color", "Accounts_OAuth_Custom_Button_Label_Color": "Button Text Color", "Accounts_OAuth_Custom_Button_Label_Text": "Button Text", diff --git a/server/lib/accounts.js b/server/lib/accounts.js index a5f0f4ac029e..16130d2d3957 100644 --- a/server/lib/accounts.js +++ b/server/lib/accounts.js @@ -9,6 +9,7 @@ import { settings } from '../../app/settings'; import { callbacks } from '../../app/callbacks'; import { Roles, Users, Settings } from '../../app/models'; import { addUserRoles } from '../../app/authorization'; +import { getAvatarSuggestionForUser } from '../../app/lib/server/functions'; const accountsConfig = { forbidClientAccountCreation: true, @@ -216,6 +217,21 @@ Accounts.insertUserDoc = _.wrap(Accounts.insertUserDoc, function(insertUserDoc, addUserRoles(_id, roles); + if (settings.get('Accounts_SetDefaultAvatar') === true) { + const avatarSuggestions = getAvatarSuggestionForUser(user); + Object.keys(avatarSuggestions).some((service) => { + const avatarData = avatarSuggestions[service]; + if (service !== 'gravatar') { + Meteor.runAsUser(_id, function() { + return Meteor.call('setAvatarFromService', avatarData.blob, '', service); + }); + return true; + } + + return false; + }); + } + return _id; });