diff --git a/packages/meteor-accounts-saml/saml_client.js b/packages/meteor-accounts-saml/saml_client.js index 87d3a533bab..325f84909e3 100644 --- a/packages/meteor-accounts-saml/saml_client.js +++ b/packages/meteor-accounts-saml/saml_client.js @@ -118,6 +118,10 @@ Meteor.loginWithSaml = function(options, callback) { Meteor.logoutWithSaml = function(options/*, callback*/) { //Accounts.saml.idpInitiatedSLO(options, callback); Meteor.call('samlLogout', options.provider, function(err, result) { + if (err || !result) { + MeteorLogout.apply(Meteor); + return; + } // A nasty bounce: 'result' has the SAML LogoutRequest but we need a proper 302 to redirected from the server. //window.location.replace(Meteor.absoluteUrl('_saml/sloRedirect/' + options.provider + '/?redirect='+result)); window.location.replace(Meteor.absoluteUrl('_saml/sloRedirect/' + options.provider + '/?redirect=' + encodeURIComponent(result))); diff --git a/packages/rocketchat-api/server/routes.coffee b/packages/rocketchat-api/server/routes.coffee index c83b04b6072..5f952dbe88d 100644 --- a/packages/rocketchat-api/server/routes.coffee +++ b/packages/rocketchat-api/server/routes.coffee @@ -237,9 +237,6 @@ RocketChat.API.v1.addRoute 'users.create', authRequired: true, user = RocketChat.models.Users.findOneById(newUserId) - if typeof @bodyParams.joinDefaultChannels is 'undefined' or @bodyParams.joinDefaultChannels - RocketChat.addUserToDefaultChannels(user) - return RocketChat.API.v1.success user: user catch e diff --git a/packages/rocketchat-cas/cas_server.js b/packages/rocketchat-cas/cas_server.js index 4a37df7c2e1..555fa638307 100644 --- a/packages/rocketchat-cas/cas_server.js +++ b/packages/rocketchat-cas/cas_server.js @@ -236,11 +236,6 @@ Accounts.registerLoginHandler(function(options) { logger.debug('Created new user for \'' + result.username + '\' with id: ' + user._id); //logger.debug(JSON.stringify(user, undefined, 4)); - logger.debug('Joining user to default channels'); - Meteor.runAsUser(user._id, function() { - Meteor.call('joinDefaultChannels'); - }); - logger.debug('Joining user to attribute channels: ' + int_attrs.rooms); if (int_attrs.rooms) { _.each(int_attrs.rooms.split(','), function(room_name) { diff --git a/packages/rocketchat-channel-settings/client/views/channelSettings.coffee b/packages/rocketchat-channel-settings/client/views/channelSettings.coffee index b4670ec92ec..54a846a21bf 100644 --- a/packages/rocketchat-channel-settings/client/views/channelSettings.coffee +++ b/packages/rocketchat-channel-settings/client/views/channelSettings.coffee @@ -143,7 +143,14 @@ Template.channelSettings.onCreated -> options: c: 'Channel' p: 'Private_Group' - canView: (room) => room.t in ['c', 'p'] + canView: (room) -> + if not room.t in ['c', 'p'] + return false + else if room.t is 'p' and not RocketChat.authz.hasAllPermission('create-c') + return false + else if room.t is 'c' and not RocketChat.authz.hasAllPermission('create-p') + return false + return true canEdit: (room) => RocketChat.authz.hasAllPermission('edit-room', room._id) save: (value, room) -> if value not in ['c', 'p'] diff --git a/packages/rocketchat-channel-settings/server/methods/saveRoomSettings.coffee b/packages/rocketchat-channel-settings/server/methods/saveRoomSettings.coffee index c2aadcadd3a..a452e3927d7 100644 --- a/packages/rocketchat-channel-settings/server/methods/saveRoomSettings.coffee +++ b/packages/rocketchat-channel-settings/server/methods/saveRoomSettings.coffee @@ -17,6 +17,12 @@ Meteor.methods room = RocketChat.models.Rooms.findOneById rid if room? + if setting is 'roomType' and value isnt room.t and value is 'c' and not RocketChat.authz.hasPermission(@userId, 'create-c') + throw new Meteor.Error 'error-action-not-allowed', 'Changing a private group to a public channel is not allowed', { method: 'saveRoomSettings', action: 'Change_Room_Type' } + + if setting is 'roomType' and value isnt room.t and value is 'p' and not RocketChat.authz.hasPermission(@userId, 'create-p') + throw new Meteor.Error 'error-action-not-allowed', 'Changing a public channel to a private room is not allowed', { method: 'saveRoomSettings', action: 'Change_Room_Type' } + switch setting when 'roomName' name = RocketChat.saveRoomName rid, value, Meteor.user() diff --git a/packages/rocketchat-crowd/server/crowd.js b/packages/rocketchat-crowd/server/crowd.js index d30fe4307d0..65c6f542726 100644 --- a/packages/rocketchat-crowd/server/crowd.js +++ b/packages/rocketchat-crowd/server/crowd.js @@ -170,10 +170,6 @@ const CROWD = class CROWD { }); } - Meteor.runAsUser(crowdUser._id, function() { - Meteor.call('joinDefaultChannels'); - }); - return { userId: crowdUser._id }; diff --git a/packages/rocketchat-custom-oauth/custom_oauth_client.coffee b/packages/rocketchat-custom-oauth/custom_oauth_client.coffee deleted file mode 100644 index f0d7338cfc7..00000000000 --- a/packages/rocketchat-custom-oauth/custom_oauth_client.coffee +++ /dev/null @@ -1,79 +0,0 @@ -# Request custom OAuth credentials for the user -# @param options {optional} -# @param credentialRequestCompleteCallback {Function} Callback function to call on -# completion. Takes one argument, credentialToken on success, or Error on -# error. -class CustomOAuth - constructor: (@name, options) -> - if not Match.test @name, String - return throw new Meteor.Error 'CustomOAuth: Name is required and must be String' - - @configure options - - Accounts.oauth.registerService @name - - @configureLogin() - - configure: (options) -> - if not Match.test options, Object - return throw new Meteor.Error 'CustomOAuth: Options is required and must be Object' - - if not Match.test options.serverURL, String - return throw new Meteor.Error 'CustomOAuth: Options.serverURL is required and must be String' - - if not Match.test options.authorizePath, String - options.authorizePath = '/oauth/authorize' - - if not Match.test options.scope, String - options.scope = 'openid' - - @serverURL = options.serverURL - @authorizePath = options.authorizePath - @scope = options.scope - - if not /^https?:\/\/.+/.test @authorizePath - @authorizePath = @serverURL + @authorizePath - - configureLogin: -> - self = @ - loginWithService = "loginWith" + s.capitalize(@name) - - Meteor[loginWithService] = (options, callback) -> - # support a callback without options - if not callback and typeof options is "function" - callback = options - options = null - - credentialRequestCompleteCallback = Accounts.oauth.credentialRequestCompleteHandler(callback) - self.requestCredential(options, credentialRequestCompleteCallback) - - requestCredential: (options, credentialRequestCompleteCallback) -> - # support both (options, callback) and (callback). - if not credentialRequestCompleteCallback and typeof options is 'function' - credentialRequestCompleteCallback = options - options = {} - - config = ServiceConfiguration.configurations.findOne service: @name - if not config - credentialRequestCompleteCallback? new ServiceConfiguration.ConfigError() - return - - credentialToken = Random.secret() - loginStyle = OAuth._loginStyle @name, config, options - - loginUrl = @authorizePath + - '?client_id=' + config.clientId + - '&redirect_uri=' + OAuth._redirectUri(@name, config) + - '&response_type=code' + - '&state=' + OAuth._stateParam(loginStyle, credentialToken, options.redirectUrl) + - '&scope=' + @scope - - OAuth.launchLogin - loginService: @name - loginStyle: loginStyle - loginUrl: loginUrl - credentialRequestCompleteCallback: credentialRequestCompleteCallback - credentialToken: credentialToken - popupOptions: - width: 900 - height: 450 diff --git a/packages/rocketchat-custom-oauth/custom_oauth_client.js b/packages/rocketchat-custom-oauth/custom_oauth_client.js new file mode 100644 index 00000000000..418bfff473f --- /dev/null +++ b/packages/rocketchat-custom-oauth/custom_oauth_client.js @@ -0,0 +1,101 @@ +/*globals OAuth*/ +// Request custom OAuth credentials for the user +// @param options {optional} +// @param credentialRequestCompleteCallback {Function} Callback function to call on +// completion. Takes one argument, credentialToken on success, or Error on +// error. + +export class CustomOAuth { + constructor(name, options) { + this.name = name; + if (!Match.test(this.name, String)) { + throw new Meteor.Error('CustomOAuth: Name is required and must be String'); + } + + this.configure(options); + + Accounts.oauth.registerService(this.name); + + this.configureLogin(); + } + + configure(options) { + if (!Match.test(options, Object)) { + throw new Meteor.Error('CustomOAuth: Options is required and must be Object'); + } + + if (!Match.test(options.serverURL, String)) { + throw new Meteor.Error('CustomOAuth: Options.serverURL is required and must be String'); + } + + if (!Match.test(options.authorizePath, String)) { + options.authorizePath = '/oauth/authorize'; + } + + if (!Match.test(options.scope, String)) { + options.scope = 'openid'; + } + + this.serverURL = options.serverURL; + this.authorizePath = options.authorizePath; + this.scope = options.scope; + + if (!/^https?:\/\/.+/.test(this.authorizePath)) { + this.authorizePath = this.serverURL + this.authorizePath; + } + } + + configureLogin() { + self = this; + const loginWithService = 'loginWith' + s.capitalize(this.name); + + Meteor[loginWithService] = (options, callback) => { + // support a callback without options + if (!callback && typeof options === 'function') { + callback = options; + options = null; + } + + const credentialRequestCompleteCallback = Accounts.oauth.credentialRequestCompleteHandler(callback); + self.requestCredential(options, credentialRequestCompleteCallback); + }; + } + + requestCredential(options, credentialRequestCompleteCallback) { + // support both (options, callback) and (callback). + if (!credentialRequestCompleteCallback && typeof options === 'function') { + credentialRequestCompleteCallback = options; + options = {}; + } + + const config = ServiceConfiguration.configurations.findOne({service: this.name}); + if (!config) { + if (credentialRequestCompleteCallback) { + credentialRequestCompleteCallback(new ServiceConfiguration.ConfigError()); + } + return; + } + + const credentialToken = Random.secret(); + const loginStyle = OAuth._loginStyle(this.name, config, options); + + const loginUrl = this.authorizePath + + '?client_id=' + config.clientId + + '&redirect_uri=' + OAuth._redirectUri(this.name, config) + + '&response_type=code' + + '&state=' + OAuth._stateParam(loginStyle, credentialToken, options.redirectUrl) + + '&scope=' + this.scope; + + OAuth.launchLogin({ + loginService: this.name, + loginStyle: loginStyle, + loginUrl: loginUrl, + credentialRequestCompleteCallback: credentialRequestCompleteCallback, + credentialToken: credentialToken, + popupOptions: { + width: 900, + height: 450 + } + }); + } +} diff --git a/packages/rocketchat-custom-oauth/custom_oauth_server.coffee b/packages/rocketchat-custom-oauth/custom_oauth_server.coffee deleted file mode 100644 index e556ff0b72e..00000000000 --- a/packages/rocketchat-custom-oauth/custom_oauth_server.coffee +++ /dev/null @@ -1,162 +0,0 @@ -Services = {} - -class CustomOAuth - constructor: (@name, options) -> - if not Match.test @name, String - return throw new Meteor.Error 'CustomOAuth: Name is required and must be String' - - if Services[@name]? - Services[@name].configure options - return - - Services[@name] = @ - - @configure options - - @userAgent = "Meteor" - if Meteor.release - @userAgent += '/' + Meteor.release - - Accounts.oauth.registerService @name - @registerService() - - configure: (options) -> - if not Match.test options, Object - return throw new Meteor.Error 'CustomOAuth: Options is required and must be Object' - - if not Match.test options.serverURL, String - return throw new Meteor.Error 'CustomOAuth: Options.serverURL is required and must be String' - - if not Match.test options.tokenPath, String - options.tokenPath = '/oauth/token' - - if not Match.test options.identityPath, String - options.identityPath = '/me' - - @serverURL = options.serverURL - @tokenPath = options.tokenPath - @identityPath = options.identityPath - @tokenSentVia = options.tokenSentVia - - if not /^https?:\/\/.+/.test @tokenPath - @tokenPath = @serverURL + @tokenPath - - if not /^https?:\/\/.+/.test @identityPath - @identityPath = @serverURL + @identityPath - - if Match.test options.addAutopublishFields, Object - Accounts.addAutopublishFields options.addAutopublishFields - - getAccessToken: (query) -> - config = ServiceConfiguration.configurations.findOne service: @name - if not config? - throw new ServiceConfiguration.ConfigError() - - response = undefined - try - response = HTTP.post @tokenPath, - auth: config.clientId + ':' + OAuth.openSecret(config.secret) - headers: - Accept: 'application/json' - 'User-Agent': @userAgent - params: - code: query.code - client_id: config.clientId - client_secret: OAuth.openSecret(config.secret) - redirect_uri: OAuth._redirectUri(@name, config) - grant_type: 'authorization_code' - state: query.state - - catch err - error = new Error("Failed to complete OAuth handshake with #{@name} at #{@tokenPath}. " + err.message) - throw _.extend error, {response: err.response} - - if response.data.error #if the http response was a json object with an error attribute - throw new Error("Failed to complete OAuth handshake with #{@name} at #{@tokenPath}. " + response.data.error) - else - return response.data.access_token - - getIdentity: (accessToken) -> - params = {} - headers = - 'User-Agent': @userAgent # http://doc.gitlab.com/ce/api/users.html#Current-user - - if @tokenSentVia is 'header' - headers['Authorization'] = 'Bearer ' + accessToken - else - params['access_token'] = accessToken - - try - response = HTTP.get @identityPath, - headers: headers - params: params - - if response.data - return response.data - else - return JSON.parse response.content - - catch err - error = new Error("Failed to fetch identity from #{@name} at #{@identityPath}. " + err.message) - throw _.extend error, {response: err.response} - - registerService: -> - self = @ - OAuth.registerService @name, 2, null, (query) -> - accessToken = self.getAccessToken query - # console.log 'at:', accessToken - - identity = self.getIdentity accessToken - - # Fix for Reddit - if identity?.result - identity = identity.result - - # Fix WordPress-like identities having 'ID' instead of 'id' - if identity?.ID and not identity.id - identity.id = identity.ID - - # Fix Auth0-like identities having 'user_id' instead of 'id' - if identity?.user_id and not identity.id - identity.id = identity.user_id - - if identity?.CharacterID and not identity.id - identity.id = identity.CharacterID - - # Fix Dataporten having 'user.userid' instead of 'id' - if identity?.user?.userid and not identity.id - identity.id = identity.user.userid - identity.email = identity.user.email - - # Fix general 'phid' instead of 'id' from phabricator - if identity?.phid and not identity.id - identity.id = identity.phid - - # Fix Keycloak-like identities having 'sub' instead of 'id' - if identity?.sub and not identity.id - identity.id = identity.sub - - # Fix general 'userid' instead of 'id' from provider - if identity?.userid and not identity.id - identity.id = identity.userid - - # console.log 'id:', JSON.stringify identity, null, ' ' - - serviceData = - _OAuthCustom: true - accessToken: accessToken - - _.extend serviceData, identity - - data = - serviceData: serviceData - options: - profile: - name: identity.name or identity.username or identity.nickname or identity.CharacterName or identity.userName or identity.preferred_username or identity.user?.name - - # console.log data - - return data - - retrieveCredential: (credentialToken, credentialSecret) -> - return OAuth.retrieveCredential credentialToken, credentialSecret diff --git a/packages/rocketchat-custom-oauth/custom_oauth_server.js b/packages/rocketchat-custom-oauth/custom_oauth_server.js new file mode 100644 index 00000000000..b71c672909c --- /dev/null +++ b/packages/rocketchat-custom-oauth/custom_oauth_server.js @@ -0,0 +1,293 @@ +/*globals OAuth*/ + +const logger = new Logger('CustomOAuth'); + +const Services = {}; +const BeforeUpdateOrCreateUserFromExternalService = []; + +export class CustomOAuth { + constructor(name, options) { + logger.debug('Init CustomOAuth', name, options); + + this.name = name; + if (!Match.test(this.name, String)) { + throw new Meteor.Error('CustomOAuth: Name is required and must be String'); + } + + if (Services[this.name]) { + Services[this.name].configure(options); + return; + } + + Services[this.name] = this; + + this.configure(options); + + this.userAgent = 'Meteor'; + if (Meteor.release) { + this.userAgent += '/' + Meteor.release; + } + + Accounts.oauth.registerService(this.name); + this.registerService(); + this.addHookToProcessUser(); + } + + configure(options) { + if (!Match.test(options, Object)) { + throw new Meteor.Error('CustomOAuth: Options is required and must be Object'); + } + + if (!Match.test(options.serverURL, String)) { + throw new Meteor.Error('CustomOAuth: Options.serverURL is required and must be String'); + } + + if (!Match.test(options.tokenPath, String)) { + options.tokenPath = '/oauth/token'; + } + + if (!Match.test(options.identityPath, String)) { + options.identityPath = '/me'; + } + + this.serverURL = options.serverURL; + this.tokenPath = options.tokenPath; + this.identityPath = options.identityPath; + this.tokenSentVia = options.tokenSentVia; + this.usernameField = (options.usernameField || '').trim(); + this.mergeUsers = options.mergeUsers; + + if (!/^https?:\/\/.+/.test(this.tokenPath)) { + this.tokenPath = this.serverURL + this.tokenPath; + } + + if (!/^https?:\/\/.+/.test(this.identityPath)) { + this.identityPath = this.serverURL + this.identityPath; + } + + if (Match.test(options.addAutopublishFields, Object)) { + Accounts.addAutopublishFields(options.addAutopublishFields); + } + } + + getAccessToken(query) { + const config = ServiceConfiguration.configurations.findOne({service: this.name}); + if (!config) { + throw new ServiceConfiguration.ConfigError(); + } + + let response = undefined; + try { + response = HTTP.post(this.tokenPath, { + auth: config.clientId + ':' + OAuth.openSecret(config.secret), + headers: { + Accept: 'application/json', + 'User-Agent': this.userAgent + }, + params: { + code: query.code, + client_id: config.clientId, + client_secret: OAuth.openSecret(config.secret), + redirect_uri: OAuth._redirectUri(this.name, config), + grant_type: 'authorization_code', + state: query.state + } + }); + } catch (err) { + const error = new Error(`Failed to complete OAuth handshake with ${this.name} at ${this.tokenPath}. ${err.message}`); + throw _.extend(error, {response: err.response}); + } + + if (response.data.error) { //if the http response was a json object with an error attribute + throw new Error(`Failed to complete OAuth handshake with ${this.name} at ${this.tokenPath}. ${response.data.error}`); + } else { + return response.data.access_token; + } + } + + getIdentity(accessToken) { + const params = {}; + const headers = { + 'User-Agent': this.userAgent // http://doc.gitlab.com/ce/api/users.html#Current-user + }; + + if (this.tokenSentVia === 'header') { + headers['Authorization'] = 'Bearer ' + accessToken; + } else { + params['access_token'] = accessToken; + } + + try { + const response = HTTP.get(this.identityPath, { + headers: headers, + params: params + }); + + let data; + + if (response.data) { + data = response.data; + } else { + data = JSON.parse(response.content); + } + + logger.debug('Identity response', JSON.stringify(data, null, 2)); + + return data; + } catch (err) { + const error = new Error(`Failed to fetch identity from ${this.name} at ${this.identityPath}. ${err.message}`); + throw _.extend(error, {response: err.response}); + } + } + + registerService() { + const self = this; + OAuth.registerService(this.name, 2, null, (query) => { + const accessToken = self.getAccessToken(query); + // console.log 'at:', accessToken + + let identity = self.getIdentity(accessToken); + + if (identity) { + // Fix for Reddit + if (identity.result) { + identity = identity.result; + } + + // Fix WordPress-like identities having 'ID' instead of 'id' + if (identity.ID && !identity.id) { + identity.id = identity.ID; + } + + // Fix Auth0-like identities having 'user_id' instead of 'id' + if (identity.user_id && !identity.id) { + identity.id = identity.user_id; + } + + if (identity.CharacterID && !identity.id) { + identity.id = identity.CharacterID; + } + + // Fix Dataporten having 'user.userid' instead of 'id' + if (identity.user && identity.user.userid && !identity.id) { + identity.id = identity.user.userid; + identity.email = identity.user.email; + } + + // Fix general 'phid' instead of 'id' from phabricator + if (identity.phid && !identity.id) { + identity.id = identity.phid; + } + + // Fix Keycloak-like identities having 'sub' instead of 'id' + if (identity.sub && !identity.id) { + identity.id = identity.sub; + } + + // Fix general 'userid' instead of 'id' from provider + if (identity.userid && !identity.id) { + identity.id = identity.userid; + } + } + + // console.log 'id:', JSON.stringify identity, null, ' ' + + const serviceData = { + _OAuthCustom: true, + accessToken: accessToken + }; + + _.extend(serviceData, identity); + + const data = { + serviceData: serviceData, + options: { + profile: { + name: identity.name || identity.username || identity.nickname || identity.CharacterName || identity.userName || identity.preferred_username || (identity.user && identity.user.name) + } + } + }; + + // console.log data + + return data; + }); + } + + retrieveCredential(credentialToken, credentialSecret) { + return OAuth.retrieveCredential(credentialToken, credentialSecret); + } + + getUsername(data) { + let username = ''; + + if (this.usernameField.indexOf('#{') > -1) { + username = this.usernameField.replace(/#{(.+?)}/g, function(match, field) { + if (!data[field]) { + throw new Meteor.Error(`Username template item "${field}" not found in data`, data); + } + return data[field]; + }); + } else { + username = data[this.usernameField]; + if (!username) { + throw new Meteor.Error(`Username field "${this.usernameField}" not found in data`, data); + } + } + + return username; + } + + addHookToProcessUser() { + BeforeUpdateOrCreateUserFromExternalService.push((serviceName, serviceData/*, options*/) => { + if (serviceName !== this.name) { + return; + } + + if (this.usernameField) { + const username = this.getUsername(serviceData); + + const user = RocketChat.models.Users.findOneByUsername(username); + if (!user) { + return; + } + + if (this.mergeUsers !== true) { + throw new Meteor.Error('CustomOAuth', `User with username ${user.username} already exists`); + } + + const serviceIdKey = `services.${serviceName}.id`; + const update = { + $set: { + [serviceIdKey]: serviceData.id + } + }; + + RocketChat.models.Users.update({_id: user._id}, update); + } + }); + + Accounts.validateNewUser((user) => { + if (!user.services || !user.services[this.name] || !user.services[this.name].id) { + return true; + } + + if (this.usernameField) { + user.username = this.getUsername(user.services[this.name]); + } + + return true; + }); + + } +} + + +const updateOrCreateUserFromExternalService = Accounts.updateOrCreateUserFromExternalService; +Accounts.updateOrCreateUserFromExternalService = function(/*serviceName, serviceData, options*/) { + for (const hook of BeforeUpdateOrCreateUserFromExternalService) { + hook.apply(this, arguments); + } + + return updateOrCreateUserFromExternalService.apply(this, arguments); +}; diff --git a/packages/rocketchat-custom-oauth/package.js b/packages/rocketchat-custom-oauth/package.js index bf9f3219d47..e4a397f479e 100644 --- a/packages/rocketchat-custom-oauth/package.js +++ b/packages/rocketchat-custom-oauth/package.js @@ -5,12 +5,12 @@ Package.describe({ }); Package.onUse(function(api) { + api.use('modules'); api.use('check'); api.use('oauth'); api.use('oauth2'); api.use('underscore'); api.use('ecmascript'); - api.use('coffeescript'); api.use('accounts-oauth'); api.use('service-configuration'); api.use('underscorestring:underscore.string'); @@ -20,9 +20,9 @@ Package.onUse(function(api) { api.use('http', 'server'); - api.addFiles('custom_oauth_client.coffee', 'client'); + api.mainModule('custom_oauth_client.js', 'client'); - api.addFiles('custom_oauth_server.coffee', 'server'); + api.mainModule('custom_oauth_server.js', 'server'); api.export('CustomOAuth'); }); diff --git a/packages/rocketchat-i18n/i18n/en.i18n.json b/packages/rocketchat-i18n/i18n/en.i18n.json index dbd01001916..883f28debc1 100644 --- a/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/packages/rocketchat-i18n/i18n/en.i18n.json @@ -56,10 +56,12 @@ "Accounts_OAuth_Custom_id": "Id", "Accounts_OAuth_Custom_Identity_Path": "Identity Path", "Accounts_OAuth_Custom_Login_Style": "Login Style", + "Accounts_OAuth_Custom_Merge_Users": "Merge users", "Accounts_OAuth_Custom_Scope": "Scope", "Accounts_OAuth_Custom_Secret": "Secret", "Accounts_OAuth_Custom_Token_Path": "Token Path", "Accounts_OAuth_Custom_Token_Sent_Via": "Token Sent Via", + "Accounts_OAuth_Custom_Username_Field": "Username field", "Accounts_OAuth_Facebook": "Facebook Login", "Accounts_OAuth_Facebook_callback_url": "Facebook Callback URL", "Accounts_OAuth_Facebook_id": "Facebook App Id", @@ -252,6 +254,7 @@ "CDN_PREFIX": "CDN Prefix", "Certificates_and_Keys": "Certificates and Keys", "Changing_email": "Changing email", + "Change_Room_Type": "Changing the Room Type", "channel": "channel", "Channel": "Channel", "Channel_already_exist": "The channel '#%s' already exists.", diff --git a/packages/rocketchat-importer-csv/server.js b/packages/rocketchat-importer-csv/server.js index 9358d9f0df6..4633d306aba 100644 --- a/packages/rocketchat-importer-csv/server.js +++ b/packages/rocketchat-importer-csv/server.js @@ -184,8 +184,7 @@ Importer.CSV = class ImporterCSV extends Importer.Base { } else { const userId = Accounts.createUser({ email: u.email, password: Date.now() + u.name + u.email.toUpperCase() }); Meteor.runAsUser(userId, () => { - Meteor.call('setUsername', u.username); - Meteor.call('joinDefaultChannels', true); + Meteor.call('setUsername', u.username, {joinDefaultChannelsSilenced: true}); RocketChat.models.Users.setName(userId, u.name); RocketChat.models.Users.update({ _id: userId }, { $addToSet: { importIds: u.id } }); u.rocketId = userId; diff --git a/packages/rocketchat-importer-hipchat-enterprise/server.js b/packages/rocketchat-importer-hipchat-enterprise/server.js index dd05cf8b405..1b6df78e817 100644 --- a/packages/rocketchat-importer-hipchat-enterprise/server.js +++ b/packages/rocketchat-importer-hipchat-enterprise/server.js @@ -256,8 +256,7 @@ Importer.HipChatEnterprise = class ImporterHipChatEnterprise extends Importer.Ba } else { const userId = Accounts.createUser({ email: u.email, password: Date.now() + u.name + u.email.toUpperCase() }); Meteor.runAsUser(userId, () => { - Meteor.call('setUsername', u.username); - Meteor.call('joinDefaultChannels', true); + Meteor.call('setUsername', u.username, {joinDefaultChannelsSilenced: true}); //TODO: Use moment timezone to calc the time offset - Meteor.call 'userSetUtcOffset', user.tz_offset / 3600 RocketChat.models.Users.setName(userId, u.name); //TODO: Think about using a custom field for the users "title" field diff --git a/packages/rocketchat-importer-hipchat/server.coffee b/packages/rocketchat-importer-hipchat/server.coffee index 80f79a5b41c..cbb6154498d 100644 --- a/packages/rocketchat-importer-hipchat/server.coffee +++ b/packages/rocketchat-importer-hipchat/server.coffee @@ -135,8 +135,7 @@ Importer.HipChat = class Importer.HipChat extends Importer.Base hipchat: "@#{user.mention_name}" rocket: "@#{user.mention_name}" Meteor.runAsUser userId, () => - Meteor.call 'setUsername', user.mention_name - Meteor.call 'joinDefaultChannels', true + Meteor.call 'setUsername', user.mention_name, {joinDefaultChannelsSilenced: true} Meteor.call 'setAvatarFromService', user.photo_url, undefined, 'url' Meteor.call 'userSetUtcOffset', parseInt moment().tz(user.timezone).format('Z').toString().split(':')[0] diff --git a/packages/rocketchat-importer-slack/server.coffee b/packages/rocketchat-importer-slack/server.coffee index 85813b34d6e..15aeeed6161 100644 --- a/packages/rocketchat-importer-slack/server.coffee +++ b/packages/rocketchat-importer-slack/server.coffee @@ -126,10 +126,9 @@ Importer.Slack = class Importer.Slack extends Importer.Base if user.profile.email userId = Accounts.createUser { email: user.profile.email, password: Date.now() + user.name + user.profile.email.toUpperCase() } else - userId = Accounts.createUser { username: user.name, password: Date.now() + user.name } + userId = Accounts.createUser { username: user.name, password: Date.now() + user.name, joinDefaultChannelsSilenced: true } Meteor.runAsUser userId, () => - Meteor.call 'setUsername', user.name - Meteor.call 'joinDefaultChannels', true + Meteor.call 'setUsername', user.name, {joinDefaultChannelsSilenced: true} url = null if user.profile.image_original url = user.profile.image_original diff --git a/packages/rocketchat-ldap/server/sync.js b/packages/rocketchat-ldap/server/sync.js index bce6c278f7a..a8b71aba2ee 100644 --- a/packages/rocketchat-ldap/server/sync.js +++ b/packages/rocketchat-ldap/server/sync.js @@ -187,11 +187,6 @@ addLdapUser = function addLdapUser(ldapUser, username, password) { syncUserData(userObject, ldapUser); - logger.info('Joining user to default channels'); - Meteor.runAsUser(userObject._id, function() { - Meteor.call('joinDefaultChannels'); - }); - return { userId: userObject._id }; diff --git a/packages/rocketchat-lib/server/functions/saveUser.js b/packages/rocketchat-lib/server/functions/saveUser.js index 632b2c55216..eee058b0230 100644 --- a/packages/rocketchat-lib/server/functions/saveUser.js +++ b/packages/rocketchat-lib/server/functions/saveUser.js @@ -51,7 +51,8 @@ RocketChat.saveUser = function(userId, userData) { // insert user const createUser = { username: userData.username, - password: userData.password + password: userData.password, + joinDefaultChannels: userData.joinDefaultChannels }; if (userData.email) { createUser.email = userData.email; @@ -76,12 +77,6 @@ RocketChat.saveUser = function(userId, userData) { Meteor.users.update({ _id: _id }, updateUser); - if (userData.joinDefaultChannels) { - Meteor.runAsUser(_id, () => { - Meteor.call('joinDefaultChannels'); - }); - } - if (userData.sendWelcomeEmail) { const header = RocketChat.placeholders.replace(RocketChat.settings.get('Email_Header') || ''); const footer = RocketChat.placeholders.replace(RocketChat.settings.get('Email_Footer') || ''); diff --git a/packages/rocketchat-lib/server/methods/addOAuthService.coffee b/packages/rocketchat-lib/server/methods/addOAuthService.coffee index b3ff589bc14..1fcd37b6972 100644 --- a/packages/rocketchat-lib/server/methods/addOAuthService.coffee +++ b/packages/rocketchat-lib/server/methods/addOAuthService.coffee @@ -9,18 +9,20 @@ Meteor.methods unless RocketChat.authz.hasPermission( Meteor.userId(), 'add-oauth-service') is true throw new Meteor.Error 'error-action-not-allowed', 'Adding OAuth Services is not allowed', { method: 'addOAuthService', action: 'Adding_OAuth_Services' } - name = name.toLowerCase().replace(/[^a-z0-9]/g, '') + name = name.toLowerCase().replace(/[^a-z0-9_]/g, '') name = s.capitalize(name) - RocketChat.settings.add "Accounts_OAuth_Custom_#{name}" , false , { type: 'boolean', group: 'OAuth', section: "Custom OAuth: #{name}", i18nLabel: 'Accounts_OAuth_Custom_Enable', persistent: true } - RocketChat.settings.add "Accounts_OAuth_Custom_#{name}_url" , '' , { type: 'string' , group: 'OAuth', section: "Custom OAuth: #{name}", i18nLabel: 'URL', persistent: true } - RocketChat.settings.add "Accounts_OAuth_Custom_#{name}_token_path" , '/oauth/token' , { type: 'string' , group: 'OAuth', section: "Custom OAuth: #{name}", i18nLabel: 'Accounts_OAuth_Custom_Token_Path', persistent: true } - RocketChat.settings.add "Accounts_OAuth_Custom_#{name}_identity_path" , '/me' , { type: 'string' , group: 'OAuth', section: "Custom OAuth: #{name}", i18nLabel: 'Accounts_OAuth_Custom_Identity_Path', persistent: true } - RocketChat.settings.add "Accounts_OAuth_Custom_#{name}_authorize_path" , '/oauth/authorize', { type: 'string' , group: 'OAuth', section: "Custom OAuth: #{name}", i18nLabel: 'Accounts_OAuth_Custom_Authorize_Path', persistent: true } - RocketChat.settings.add "Accounts_OAuth_Custom_#{name}_scope" , 'openid' , { type: 'string' , group: 'OAuth', section: "Custom OAuth: #{name}", i18nLabel: 'Accounts_OAuth_Custom_Scope', persistent: true } - RocketChat.settings.add "Accounts_OAuth_Custom_#{name}_token_sent_via" , 'payload' , { type: 'select' , group: 'OAuth', section: "Custom OAuth: #{name}", i18nLabel: 'Accounts_OAuth_Custom_Token_Sent_Via', persistent: true, values: [ { key: 'header', i18nLabel: 'Header' }, { key: 'payload', i18nLabel: 'Payload' } ] } - RocketChat.settings.add "Accounts_OAuth_Custom_#{name}_id" , '' , { type: 'string' , group: 'OAuth', section: "Custom OAuth: #{name}", i18nLabel: 'Accounts_OAuth_Custom_id', persistent: true } - RocketChat.settings.add "Accounts_OAuth_Custom_#{name}_secret" , '' , { type: 'string' , group: 'OAuth', section: "Custom OAuth: #{name}", i18nLabel: 'Accounts_OAuth_Custom_Secret', persistent: true } - RocketChat.settings.add "Accounts_OAuth_Custom_#{name}_login_style" , 'popup' , { type: 'select' , group: 'OAuth', section: "Custom OAuth: #{name}", i18nLabel: 'Accounts_OAuth_Custom_Login_Style', persistent: true, values: [ { key: 'redirect', i18nLabel: 'Redirect' }, { key: 'popup', i18nLabel: 'Popup' }, { key: '', i18nLabel: 'Default' } ] } - RocketChat.settings.add "Accounts_OAuth_Custom_#{name}_button_label_text" , '' , { type: 'string' , group: 'OAuth', section: "Custom OAuth: #{name}", i18nLabel: 'Accounts_OAuth_Custom_Button_Label_Text', persistent: true } - RocketChat.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 } - RocketChat.settings.add "Accounts_OAuth_Custom_#{name}_button_color" , '#13679A' , { type: 'string' , group: 'OAuth', section: "Custom OAuth: #{name}", i18nLabel: 'Accounts_OAuth_Custom_Button_Color', persistent: true } + RocketChat.settings.add "Accounts_OAuth_Custom-#{name}" , false , { type: 'boolean', group: 'OAuth', section: "Custom OAuth: #{name}", i18nLabel: 'Accounts_OAuth_Custom_Enable', persistent: true } + RocketChat.settings.add "Accounts_OAuth_Custom-#{name}-url" , '' , { type: 'string' , group: 'OAuth', section: "Custom OAuth: #{name}", i18nLabel: 'URL', persistent: true } + RocketChat.settings.add "Accounts_OAuth_Custom-#{name}-token_path" , '/oauth/token' , { type: 'string' , group: 'OAuth', section: "Custom OAuth: #{name}", i18nLabel: 'Accounts_OAuth_Custom_Token_Path', persistent: true } + RocketChat.settings.add "Accounts_OAuth_Custom-#{name}-identity_path" , '/me' , { type: 'string' , group: 'OAuth', section: "Custom OAuth: #{name}", i18nLabel: 'Accounts_OAuth_Custom_Identity_Path', persistent: true } + RocketChat.settings.add "Accounts_OAuth_Custom-#{name}-authorize_path" , '/oauth/authorize', { type: 'string' , group: 'OAuth', section: "Custom OAuth: #{name}", i18nLabel: 'Accounts_OAuth_Custom_Authorize_Path', persistent: true } + RocketChat.settings.add "Accounts_OAuth_Custom-#{name}-scope" , 'openid' , { type: 'string' , group: 'OAuth', section: "Custom OAuth: #{name}", i18nLabel: 'Accounts_OAuth_Custom_Scope', persistent: true } + RocketChat.settings.add "Accounts_OAuth_Custom-#{name}-token_sent_via" , 'payload' , { type: 'select' , group: 'OAuth', section: "Custom OAuth: #{name}", i18nLabel: 'Accounts_OAuth_Custom_Token_Sent_Via', persistent: true, values: [ { key: 'header', i18nLabel: 'Header' }, { key: 'payload', i18nLabel: 'Payload' } ] } + RocketChat.settings.add "Accounts_OAuth_Custom-#{name}-id" , '' , { type: 'string' , group: 'OAuth', section: "Custom OAuth: #{name}", i18nLabel: 'Accounts_OAuth_Custom_id', persistent: true } + RocketChat.settings.add "Accounts_OAuth_Custom-#{name}-secret" , '' , { type: 'string' , group: 'OAuth', section: "Custom OAuth: #{name}", i18nLabel: 'Accounts_OAuth_Custom_Secret', persistent: true } + RocketChat.settings.add "Accounts_OAuth_Custom-#{name}-login_style" , 'popup' , { type: 'select' , group: 'OAuth', section: "Custom OAuth: #{name}", i18nLabel: 'Accounts_OAuth_Custom_Login_Style', persistent: true, values: [ { key: 'redirect', i18nLabel: 'Redirect' }, { key: 'popup', i18nLabel: 'Popup' }, { key: '', i18nLabel: 'Default' } ] } + RocketChat.settings.add "Accounts_OAuth_Custom-#{name}-button_label_text" , '' , { type: 'string' , group: 'OAuth', section: "Custom OAuth: #{name}", i18nLabel: 'Accounts_OAuth_Custom_Button_Label_Text', persistent: true } + RocketChat.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 } + RocketChat.settings.add "Accounts_OAuth_Custom-#{name}-button_color" , '#13679A' , { type: 'string' , group: 'OAuth', section: "Custom OAuth: #{name}", i18nLabel: 'Accounts_OAuth_Custom_Button_Color', persistent: true } + RocketChat.settings.add "Accounts_OAuth_Custom-#{name}-username_field" , '' , { type: 'string' , group: 'OAuth', section: "Custom OAuth: #{name}", i18nLabel: 'Accounts_OAuth_Custom_Username_Field', persistent: true } + RocketChat.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/packages/rocketchat-lib/server/methods/removeOAuthService.coffee b/packages/rocketchat-lib/server/methods/removeOAuthService.coffee index 689609246ec..13ba3a2afb8 100644 --- a/packages/rocketchat-lib/server/methods/removeOAuthService.coffee +++ b/packages/rocketchat-lib/server/methods/removeOAuthService.coffee @@ -9,18 +9,20 @@ Meteor.methods unless RocketChat.authz.hasPermission( Meteor.userId(), 'add-oauth-service') is true throw new Meteor.Error 'error-not-allowed', 'Not allowed', { method: 'removeOAuthService' } - name = name.toLowerCase().replace(/[^a-z0-9]/g, '') + name = name.toLowerCase().replace(/[^a-z0-9_]/g, '') name = s.capitalize(name) - RocketChat.settings.removeById "Accounts_OAuth_Custom_#{name}" - RocketChat.settings.removeById "Accounts_OAuth_Custom_#{name}_url" - RocketChat.settings.removeById "Accounts_OAuth_Custom_#{name}_token_path" - RocketChat.settings.removeById "Accounts_OAuth_Custom_#{name}_identity_path" - RocketChat.settings.removeById "Accounts_OAuth_Custom_#{name}_authorize_path" - RocketChat.settings.removeById "Accounts_OAuth_Custom_#{name}_scope" - RocketChat.settings.removeById "Accounts_OAuth_Custom_#{name}_token_sent_via" - RocketChat.settings.removeById "Accounts_OAuth_Custom_#{name}_id" - RocketChat.settings.removeById "Accounts_OAuth_Custom_#{name}_secret" - RocketChat.settings.removeById "Accounts_OAuth_Custom_#{name}_button_label_text" - RocketChat.settings.removeById "Accounts_OAuth_Custom_#{name}_button_label_color" - RocketChat.settings.removeById "Accounts_OAuth_Custom_#{name}_button_color" - RocketChat.settings.removeById "Accounts_OAuth_Custom_#{name}_login_style" + RocketChat.settings.removeById "Accounts_OAuth_Custom-#{name}" + RocketChat.settings.removeById "Accounts_OAuth_Custom-#{name}-url" + RocketChat.settings.removeById "Accounts_OAuth_Custom-#{name}-token_path" + RocketChat.settings.removeById "Accounts_OAuth_Custom-#{name}-identity_path" + RocketChat.settings.removeById "Accounts_OAuth_Custom-#{name}-authorize_path" + RocketChat.settings.removeById "Accounts_OAuth_Custom-#{name}-scope" + RocketChat.settings.removeById "Accounts_OAuth_Custom-#{name}-token_sent_via" + RocketChat.settings.removeById "Accounts_OAuth_Custom-#{name}-id" + RocketChat.settings.removeById "Accounts_OAuth_Custom-#{name}-secret" + RocketChat.settings.removeById "Accounts_OAuth_Custom-#{name}-button_label_text" + RocketChat.settings.removeById "Accounts_OAuth_Custom-#{name}-button_label_color" + RocketChat.settings.removeById "Accounts_OAuth_Custom-#{name}-button_color" + RocketChat.settings.removeById "Accounts_OAuth_Custom-#{name}-login_style" + RocketChat.settings.removeById "Accounts_OAuth_Custom-#{name}-username_field" + RocketChat.settings.removeById "Accounts_OAuth_Custom-#{name}-merge_users" diff --git a/packages/rocketchat-lib/server/methods/setUsername.coffee b/packages/rocketchat-lib/server/methods/setUsername.coffee index 3438c74b2f1..af260d1eac3 100644 --- a/packages/rocketchat-lib/server/methods/setUsername.coffee +++ b/packages/rocketchat-lib/server/methods/setUsername.coffee @@ -1,5 +1,5 @@ Meteor.methods - setUsername: (username) -> + setUsername: (username, {joinDefaultChannelsSilenced}={}) -> check username, String @@ -33,6 +33,10 @@ Meteor.methods unless RocketChat.setUsername user._id, username throw new Meteor.Error 'error-could-not-change-username', "Could not change username", { method: 'setUsername' } + if not user.username? + Meteor.runAsUser user._id, -> + Meteor.call('joinDefaultChannels', joinDefaultChannelsSilenced) + return username RocketChat.RateLimiter.limitMethod 'setUsername', 1, 1000, diff --git a/packages/rocketchat-lib/server/startup/oAuthServicesUpdate.coffee b/packages/rocketchat-lib/server/startup/oAuthServicesUpdate.coffee index d59f2370fdd..d5fb356dc47 100644 --- a/packages/rocketchat-lib/server/startup/oAuthServicesUpdate.coffee +++ b/packages/rocketchat-lib/server/startup/oAuthServicesUpdate.coffee @@ -8,7 +8,7 @@ OAuthServicesUpdate = -> Meteor.clearTimeout timer if timer? timer = Meteor.setTimeout -> - services = RocketChat.settings.get(/^(Accounts_OAuth_|Accounts_OAuth_Custom_)[a-z0-9_-]+$/i) + services = RocketChat.settings.get(/^(Accounts_OAuth_|Accounts_OAuth_Custom-)[a-z0-9_]+$/i) for service in services logger.oauth_updated service.key @@ -16,8 +16,8 @@ OAuthServicesUpdate = -> if serviceName is 'Meteor' serviceName = 'meteor-developer' - if /Accounts_OAuth_Custom_/.test service.key - serviceName = service.key.replace('Accounts_OAuth_Custom_', '') + if /Accounts_OAuth_Custom-/.test service.key + serviceName = service.key.replace('Accounts_OAuth_Custom-', '') if service.value is true data = @@ -25,18 +25,22 @@ OAuthServicesUpdate = -> secret: RocketChat.settings.get("#{service.key}_secret") - if /Accounts_OAuth_Custom_/.test service.key + if /Accounts_OAuth_Custom-/.test service.key data.custom = true - data.serverURL = RocketChat.settings.get("#{service.key}_url") - data.tokenPath = RocketChat.settings.get("#{service.key}_token_path") - data.identityPath = RocketChat.settings.get("#{service.key}_identity_path") - data.authorizePath = RocketChat.settings.get("#{service.key}_authorize_path") - data.scope = RocketChat.settings.get("#{service.key}_scope") - data.buttonLabelText = RocketChat.settings.get("#{service.key}_button_label_text") - data.buttonLabelColor = RocketChat.settings.get("#{service.key}_button_label_color") - data.loginStyle = RocketChat.settings.get("#{service.key}_login_style") - data.buttonColor = RocketChat.settings.get("#{service.key}_button_color") - data.tokenSentVia = RocketChat.settings.get("#{service.key}_token_sent_via") + data.clientId = RocketChat.settings.get("#{service.key}-id") + data.secret = RocketChat.settings.get("#{service.key}-secret") + data.serverURL = RocketChat.settings.get("#{service.key}-url") + data.tokenPath = RocketChat.settings.get("#{service.key}-token_path") + data.identityPath = RocketChat.settings.get("#{service.key}-identity_path") + data.authorizePath = RocketChat.settings.get("#{service.key}-authorize_path") + data.scope = RocketChat.settings.get("#{service.key}-scope") + data.buttonLabelText = RocketChat.settings.get("#{service.key}-button_label_text") + data.buttonLabelColor = RocketChat.settings.get("#{service.key}-button_label_color") + data.loginStyle = RocketChat.settings.get("#{service.key}-login_style") + data.buttonColor = RocketChat.settings.get("#{service.key}-button_color") + data.tokenSentVia = RocketChat.settings.get("#{service.key}-token_sent_via") + data.usernameField = RocketChat.settings.get("#{service.key}-username_field") + data.mergeUsers = RocketChat.settings.get("#{service.key}-merge_users") new CustomOAuth serviceName.toLowerCase(), serverURL: data.serverURL tokenPath: data.tokenPath @@ -45,6 +49,8 @@ OAuthServicesUpdate = -> scope: data.scope loginStyle: data.loginStyle tokenSentVia: data.tokenSentVia + usernameField: data.usernameField + mergeUsers: data.mergeUsers if serviceName is 'Facebook' data.appId = data.clientId @@ -60,13 +66,13 @@ OAuthServicesUpdate = -> OAuthServicesRemove = (_id) -> - serviceName = _id.replace('Accounts_OAuth_Custom_', '') + serviceName = _id.replace('Accounts_OAuth_Custom-', '') ServiceConfiguration.configurations.remove {service: serviceName.toLowerCase()} RocketChat.settings.get /^Accounts_OAuth_.+/, (key, value) -> OAuthServicesUpdate() -RocketChat.settings.get /^Accounts_OAuth_Custom.+/, (key, value) -> +RocketChat.settings.get /^Accounts_OAuth_Custom-[a-z0-9_]+/, (key, value) -> if not value OAuthServicesRemove key diff --git a/packages/rocketchat-livechat/app/client/lib/_livechat.js b/packages/rocketchat-livechat/app/client/lib/_livechat.js index f1c361bcbdc..df58ad9821c 100644 --- a/packages/rocketchat-livechat/app/client/lib/_livechat.js +++ b/packages/rocketchat-livechat/app/client/lib/_livechat.js @@ -24,6 +24,8 @@ this.Livechat = new (class Livechat { this._department = new ReactiveVar(null); + this._ready = new ReactiveVar(false); + Tracker.autorun(() => { if (this._room.get() && Meteor.userId()) { RoomHistoryManager.getMoreIfIsEmpty(this._room.get()); @@ -140,4 +142,12 @@ this.Livechat = new (class Livechat { this._department.set(dept._id); } } + + ready() { + this._ready.set(true); + } + + isReady() { + return this._ready.get(); + } })(); diff --git a/packages/rocketchat-livechat/app/client/lib/hooks.js b/packages/rocketchat-livechat/app/client/lib/hooks.js index 6733cb2f6ef..65f106dc71b 100644 --- a/packages/rocketchat-livechat/app/client/lib/hooks.js +++ b/packages/rocketchat-livechat/app/client/lib/hooks.js @@ -41,5 +41,10 @@ window.addEventListener('message', function(msg) { // tell parent window that we are ready Meteor.startup(function() { - parentCall('ready'); + Tracker.autorun((c) => { + if (Livechat.isReady()) { + parentCall('ready'); + c.stop(); + } + }); }); diff --git a/packages/rocketchat-livechat/app/client/views/livechatWindow.js b/packages/rocketchat-livechat/app/client/views/livechatWindow.js index 8785b0fe2fe..381ba5e8704 100644 --- a/packages/rocketchat-livechat/app/client/views/livechatWindow.js +++ b/packages/rocketchat-livechat/app/client/views/livechatWindow.js @@ -113,6 +113,8 @@ Template.livechatWindow.onCreated(function() { result.departments.forEach((department) => { Department.insert(department); }); + + Livechat.ready(); } }); }); diff --git a/packages/rocketchat-slackbridge/.npm/package/npm-shrinkwrap.json b/packages/rocketchat-slackbridge/.npm/package/npm-shrinkwrap.json index 20e2ee7276f..093df3799e7 100644 --- a/packages/rocketchat-slackbridge/.npm/package/npm-shrinkwrap.json +++ b/packages/rocketchat-slackbridge/.npm/package/npm-shrinkwrap.json @@ -91,8 +91,8 @@ "from": "cycle@>=1.0.0 <1.1.0" }, "dashdash": { - "version": "1.14.0", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.0.tgz", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", "from": "dashdash@>=1.12.0 <2.0.0", "dependencies": { "assert-plus": { @@ -103,8 +103,8 @@ } }, "debug": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz", "from": "debug@>=2.0.0 <3.0.0" }, "delayed-stream": { @@ -148,8 +148,8 @@ "from": "forever-agent@>=0.6.1 <0.7.0" }, "form-data": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.1.tgz", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.2.tgz", "from": "form-data@>=2.1.1 <2.2.0" }, "generate-function": { @@ -270,24 +270,19 @@ "from": "lodash@>=3.10.1 <4.0.0" }, "mime-db": { - "version": "1.24.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.24.0.tgz", - "from": "mime-db@>=1.24.0 <1.25.0" + "version": "1.25.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.25.0.tgz", + "from": "mime-db@>=1.25.0 <1.26.0" }, "mime-types": { - "version": "2.1.12", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.12.tgz", + "version": "2.1.13", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.13.tgz", "from": "mime-types@>=2.1.7 <2.2.0" }, "ms": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", - "from": "ms@0.7.1" - }, - "node-uuid": { - "version": "1.4.7", - "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.7.tgz", - "from": "node-uuid@>=1.4.7 <1.5.0" + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz", + "from": "ms@0.7.2" }, "oauth-sign": { "version": "0.8.2", @@ -309,11 +304,6 @@ "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", "from": "pinkie-promise@>=2.0.0 <3.0.0" }, - "pkginfo": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/pkginfo/-/pkginfo-0.3.1.tgz", - "from": "pkginfo@>=0.3.0 <0.4.0" - }, "punycode": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", @@ -325,8 +315,8 @@ "from": "qs@>=6.3.0 <6.4.0" }, "request": { - "version": "2.76.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.76.0.tgz", + "version": "2.79.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.79.0.tgz", "from": "request@>=2.64.0 <3.0.0" }, "retry": { @@ -340,9 +330,9 @@ "from": "semver@>=5.0.1 <5.1.0" }, "slack-client": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/slack-client/-/slack-client-2.0.4.tgz", - "from": "slack-client@2.0.4" + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/slack-client/-/slack-client-2.0.6.tgz", + "from": "slack-client@2.0.6" }, "sntp": { "version": "1.0.9", @@ -392,8 +382,8 @@ "from": "tunnel-agent@>=0.4.1 <0.5.0" }, "tweetnacl": { - "version": "0.14.3", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.3.tgz", + "version": "0.14.4", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.4.tgz", "from": "tweetnacl@>=0.14.0 <0.15.0" }, "ultron": { @@ -406,14 +396,19 @@ "resolved": "https://registry.npmjs.org/url-join/-/url-join-0.0.1.tgz", "from": "url-join@0.0.1" }, + "uuid": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.0.1.tgz", + "from": "uuid@>=3.0.0 <4.0.0" + }, "verror": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/verror/-/verror-1.3.6.tgz", "from": "verror@1.3.6" }, "winston": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/winston/-/winston-2.2.0.tgz", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/winston/-/winston-2.3.0.tgz", "from": "winston@>=2.1.1 <3.0.0", "dependencies": { "async": { diff --git a/packages/rocketchat-slackbridge/package.js b/packages/rocketchat-slackbridge/package.js index 391129f9f38..fc18d4da1b7 100644 --- a/packages/rocketchat-slackbridge/package.js +++ b/packages/rocketchat-slackbridge/package.js @@ -20,5 +20,5 @@ Package.onUse(function(api) { }); Npm.depends({ - 'slack-client': '2.0.4' + 'slack-client': '2.0.6' }); diff --git a/packages/rocketchat-slackbridge/slackbridge.js b/packages/rocketchat-slackbridge/slackbridge.js index ebb041d2918..9dbc6a501a2 100644 --- a/packages/rocketchat-slackbridge/slackbridge.js +++ b/packages/rocketchat-slackbridge/slackbridge.js @@ -197,15 +197,21 @@ class SlackBridge { userData.rocketId = existingUser._id; userData.name = existingUser.username; } else { - let newUser = { password: Random.id() }; - if (isBot || !email) { - newUser.username = userData.name; - } else { + let newUser = { + password: Random.id(), + username: userData.name + }; + + if (!isBot && email) { newUser.email = email; } + + if (isBot) { + newUser.joinDefaultChannels = false; + } + userData.rocketId = Accounts.createUser(newUser); let userUpdate = { - username: userData.name, utcOffset: userData.tz_offset / 3600, // Slack's is -18000 which translates to Rocket.Chat's after dividing by 3600, roles: isBot ? [ 'bot' ] : [ 'user' ] }; @@ -238,7 +244,6 @@ class SlackBridge { logger.class.debug('Error setting user avatar', error.message); } } - RocketChat.addUserToDefaultChannels(user, true); } let importIds = [ userData.id ]; diff --git a/packages/rocketchat-ui-account/account/accountFlex.html b/packages/rocketchat-ui-account/account/accountFlex.html index 759372dd0a7..6c3e46ddcd6 100644 --- a/packages/rocketchat-ui-account/account/accountFlex.html +++ b/packages/rocketchat-ui-account/account/accountFlex.html @@ -9,13 +9,16 @@