Merge branch 'develop' into experimental

pull/4729/head
Gabriel Engel 9 years ago
commit 362b4e18fd
  1. 4
      packages/meteor-accounts-saml/saml_client.js
  2. 3
      packages/rocketchat-api/server/routes.coffee
  3. 5
      packages/rocketchat-cas/cas_server.js
  4. 9
      packages/rocketchat-channel-settings/client/views/channelSettings.coffee
  5. 6
      packages/rocketchat-channel-settings/server/methods/saveRoomSettings.coffee
  6. 4
      packages/rocketchat-crowd/server/crowd.js
  7. 79
      packages/rocketchat-custom-oauth/custom_oauth_client.coffee
  8. 101
      packages/rocketchat-custom-oauth/custom_oauth_client.js
  9. 162
      packages/rocketchat-custom-oauth/custom_oauth_server.coffee
  10. 293
      packages/rocketchat-custom-oauth/custom_oauth_server.js
  11. 6
      packages/rocketchat-custom-oauth/package.js
  12. 3
      packages/rocketchat-i18n/i18n/en.i18n.json
  13. 3
      packages/rocketchat-importer-csv/server.js
  14. 3
      packages/rocketchat-importer-hipchat-enterprise/server.js
  15. 3
      packages/rocketchat-importer-hipchat/server.coffee
  16. 5
      packages/rocketchat-importer-slack/server.coffee
  17. 5
      packages/rocketchat-ldap/server/sync.js
  18. 9
      packages/rocketchat-lib/server/functions/saveUser.js
  19. 30
      packages/rocketchat-lib/server/methods/addOAuthService.coffee
  20. 30
      packages/rocketchat-lib/server/methods/removeOAuthService.coffee
  21. 6
      packages/rocketchat-lib/server/methods/setUsername.coffee
  22. 38
      packages/rocketchat-lib/server/startup/oAuthServicesUpdate.coffee
  23. 10
      packages/rocketchat-livechat/app/client/lib/_livechat.js
  24. 7
      packages/rocketchat-livechat/app/client/lib/hooks.js
  25. 2
      packages/rocketchat-livechat/app/client/views/livechatWindow.js
  26. 61
      packages/rocketchat-slackbridge/.npm/package/npm-shrinkwrap.json
  27. 2
      packages/rocketchat-slackbridge/package.js
  28. 17
      packages/rocketchat-slackbridge/slackbridge.js
  29. 5
      packages/rocketchat-ui-account/account/accountFlex.html
  30. 3
      packages/rocketchat-ui-login/username/username.coffee
  31. 2
      packages/rocketchat-ui-message/message/messageBox.coffee
  32. 5
      server/lib/accounts.coffee
  33. 7
      server/restapi/restapi.coffee
  34. 12
      server/startup/migrations/v069.coffee
  35. 14
      server/startup/migrations/v070.js
  36. 38
      server/stream/streamBroadcast.coffee

@ -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)));

@ -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

@ -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) {

@ -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']

@ -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()

@ -170,10 +170,6 @@ const CROWD = class CROWD {
});
}
Meteor.runAsUser(crowdUser._id, function() {
Meteor.call('joinDefaultChannels');
});
return {
userId: crowdUser._id
};

@ -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

@ -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
}
});
}
}

@ -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

@ -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);
};

@ -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');
});

@ -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.",

@ -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;

@ -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

@ -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]

@ -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

@ -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
};

@ -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') || '');

@ -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 }

@ -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"

@ -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,

@ -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

@ -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();
}
})();

@ -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();
}
});
});

@ -113,6 +113,8 @@ Template.livechatWindow.onCreated(function() {
result.departments.forEach((department) => {
Department.insert(department);
});
Livechat.ready();
}
});
});

@ -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": {

@ -20,5 +20,5 @@ Package.onUse(function(api) {
});
Npm.depends({
'slack-client': '2.0.4'
'slack-client': '2.0.6'
});

@ -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 ];

@ -9,13 +9,16 @@
<ul>
<li>
<a href="{{pathFor 'account' group='preferences'}}" class="account-link">{{_ "Preferences"}}</a>
</li>
<li>
{{#if allowUserProfileChange}}
<a href="{{pathFor 'account' group='profile'}}" class="account-link">{{_ "Profile"}}</a>
{{/if}}
</li>
<li>
{{#if allowUserAvatarChange}}
<a href="{{pathFor 'changeAvatar'}}" class="account-link">{{_ "Avatar"}}</a>
{{/if}}
{{!-- move this to profile --}}
</li>
</ul>
</div>

@ -52,6 +52,3 @@ Template.username.events
RocketChat.Button.reset(button)
instance.username.set(username)
RocketChat.callbacks.run('usernameSet')
if not err?
Meteor.call 'joinDefaultChannels'

@ -191,7 +191,7 @@ Template.messageBox.events
fileUpload filesToUpload
'click .message-form .geo-location': (event, instance) ->
'click .message-form .message-buttons.location': (event, instance) ->
roomId = @_id
position = RocketChat.Geolocation.get()

@ -81,6 +81,11 @@ Accounts.insertUserDoc = _.wrap Accounts.insertUserDoc, (insertUserDoc, options,
_id = insertUserDoc.call(Accounts, options, user)
# Add user to default channels
if user.username? and options.joinDefaultChannels isnt false and user.joinDefaultChannels isnt false
Meteor.runAsUser _id, ->
Meteor.call('joinDefaultChannels', options.joinDefaultChannelsSilenced)
if roles.length is 0
# when inserting first user give them admin privileges otherwise make a regular user
hasAdmin = RocketChat.models.Users.findOne({ roles: 'admin' }, {fields: {_id: 1}})

@ -157,7 +157,6 @@ Api.addRoute 'bulk/register', authRequired: true,
ids[i] = {uid: Meteor.call 'registerUser', incoming}
Meteor.runAsUser ids[i].uid, () =>
Meteor.call 'setUsername', incoming.name
Meteor.call 'joinDefaultChannels'
status: 'success', ids: ids
catch e
@ -233,7 +232,7 @@ Api.addRoute 'bulk/createRoom', authRequired: true,
Meteor.runAsUser this.userId, () =>
(if incoming.private
ids[i] = Meteor.call 'createPrivateGroup', incoming.name, incoming.members
else
else
ids[i] = Meteor.call 'createChannel', incoming.name, incoming.members) for incoming,i in @bodyParams.rooms
status: 'success', ids: ids # need to handle error
catch e
@ -261,12 +260,12 @@ Api.addRoute 'room/:id/archive', authRequired: true,
console.log '[restapi] archiveRoom -> '.red, "User does not have 'archive-room' permission"
statusCode: 403
body: status: 'error', message: 'You do not have permission to do this'
# unarchive a room by it's ID
Api.addRoute 'room/:id/unarchive', authRequired: true,
post:
action: ->
# user must also have unarchive-room permission
# user must also have unarchive-room permission
if RocketChat.authz.hasPermission(@userId, 'unarchive-room')
try
Meteor.runAsUser this.userId, () =>

@ -0,0 +1,12 @@
RocketChat.Migrations.add
version: 69
up: ->
RocketChat.models.Settings.update { "_id": "theme-color-custom-scrollbar-color", "value": "rgba(255, 255, 255, 0.05)" }, { $set: { "editor": "expression", "value": "@transparent-darker" } }
RocketChat.models.Settings.update { "_id": "theme-color-info-font-color", "value": "#aaaaaa" }, { $set: { "editor": "expression", "value": "@secondary-font-color" } }
RocketChat.models.Settings.update { "_id": "theme-color-link-font-color", "value": "#008ce3" }, { $set: { "editor": "expression", "value": "@primary-action-color" } }
RocketChat.models.Settings.update { "_id": "theme-color-status-away", "value": "#fcb316" }, { $set: { "editor": "expression", "value": "@pending-color" } }
RocketChat.models.Settings.update { "_id": "theme-color-status-busy", "value": "#d30230" }, { $set: { "editor": "expression", "value": "@error-color" } }
RocketChat.models.Settings.update { "_id": "theme-color-status-offline", "value": "rgba(150, 150, 150, 0.50)" }, { $set: { "editor": "expression", "value": "@transparent-darker" } }
RocketChat.models.Settings.update { "_id": "theme-color-status-online", "value": "#35ac19" }, { $set: { "editor": "expression", "value": "@success-color" } }
RocketChat.models.Settings.update { "_id": "theme-color-tertiary-background-color", "value": "#eaeaea" }, { $set: { "editor": "expression", "value": "@component-color" } }
RocketChat.models.Settings.update { "_id": "theme-color-tertiary-font-color", "value": "rgba(255, 255, 255, 0.6)" }, { $set: { "editor": "expression", "value": "@transparent-lightest" } }

@ -0,0 +1,14 @@
RocketChat.Migrations.add({
version: 70,
up: function() {
const settings = RocketChat.models.Settings.find({ _id: /^Accounts_OAuth_Custom_.+/ }).fetch();
for (const setting of settings) {
const _id = setting._id;
setting._id = setting._id.replace(/Accounts_OAuth_Custom_([A-Za-z0-9]+)_(.+)/, 'Accounts_OAuth_Custom-$1-$2');
setting._id = setting._id.replace(/Accounts_OAuth_Custom_([A-Za-z0-9]+)/, 'Accounts_OAuth_Custom-$1');
RocketChat.models.Settings.remove({ _id: _id });
RocketChat.models.Settings.insert(setting);
}
}
});

@ -61,32 +61,32 @@ startMatrixBroadcast = ->
connections[instance].disconnect()
delete connections[instance]
Meteor.methods
broadcastAuth: (remoteId, selfId) ->
check selfId, String
check remoteId, String
Meteor.methods
broadcastAuth: (remoteId, selfId) ->
check selfId, String
check remoteId, String
@unblock()
if selfId is InstanceStatus.id() and remoteId isnt InstanceStatus.id() and InstanceStatus.getCollection().findOne({_id: remoteId})?
@connection.broadcastAuth = true
@unblock()
if selfId is InstanceStatus.id() and remoteId isnt InstanceStatus.id() and InstanceStatus.getCollection().findOne({_id: remoteId})?
@connection.broadcastAuth = true
return @connection.broadcastAuth is true
return @connection.broadcastAuth is true
stream: (streamName, eventName, args) ->
# Prevent call from self and client
if not @connection?
return 'self-not-authorized'
stream: (streamName, eventName, args) ->
# Prevent call from self and client
if not @connection?
return 'self-not-authorized'
# Prevent call from unauthrorized connections
if @connection.broadcastAuth isnt true
return 'not-authorized'
# Prevent call from unauthrorized connections
if @connection.broadcastAuth isnt true
return 'not-authorized'
if not Meteor.StreamerCentral.instances[streamName]?
return 'stream-not-exists'
if not Meteor.StreamerCentral.instances[streamName]?
return 'stream-not-exists'
Meteor.StreamerCentral.instances[streamName]._emit(eventName, args)
Meteor.StreamerCentral.instances[streamName]._emit(eventName, args)
return undefined
return undefined
startStreamCastBroadcast = (value) ->
instance = 'StreamCast'

Loading…
Cancel
Save