Skip to content
Snippets Groups Projects
Unverified Commit 69d88cda authored by Diego Sampaio's avatar Diego Sampaio
Browse files

Merge branch 'develop' into fix-email-link

parents c95be3ee 0f5e4c04
No related merge requests found
Showing
with 407 additions and 7 deletions
......@@ -2,6 +2,7 @@
**/build/*
**/node_modules/*
**/tmp/*
**/.meteor/dev_bundle
/private/certs/*
*.bak
*.iml
......
......@@ -45,6 +45,7 @@ rocketchat:cas
rocketchat:channel-settings
rocketchat:channel-settings-mail-messages
rocketchat:colors
rocketchat:crowd
rocketchat:custom-oauth
rocketchat:emojione
rocketchat:favico
......@@ -153,3 +154,5 @@ underscorestring:underscore.string
yasaricli:slugify
yasinuslu:blaze-meta
rocketchat:slashcommands-open
mdg:geolocation
rocketchat:mapview
......@@ -74,6 +74,7 @@ livedata@1.0.15
localstorage@1.0.5
logging@1.0.8
matb33:collection-hooks@0.8.1
mdg:geolocation@1.3.0
mdg:validation-error@0.5.1
meteor@1.1.10
meteor-base@1.0.1
......@@ -138,6 +139,7 @@ rocketchat:channel-settings@0.0.1
rocketchat:channel-settings-mail-messages@0.0.1
rocketchat:colors@0.0.1
rocketchat:cors@0.0.1
rocketchat:crowd@1.0.0
rocketchat:custom-oauth@1.0.0
rocketchat:emojione@0.0.1
rocketchat:favico@0.0.1
......@@ -158,6 +160,7 @@ rocketchat:lib@0.0.1
rocketchat:livechat@0.0.1
rocketchat:logger@0.0.1
rocketchat:mailer@0.0.1
rocketchat:mapview@0.0.1
rocketchat:markdown@0.0.1
rocketchat:mentions@0.0.1
rocketchat:mentions-flextab@0.0.1
......
......@@ -19,7 +19,7 @@ const pkgdef :Spk.PackageDefinition = (
appTitle = (defaultText = "Rocket.Chat"),
appVersion = 36, # Increment this for every release.
appVersion = 38, # Increment this for every release.
appMarketingVersion = (defaultText = "0.36.0"),
# Human-readable representation of appVersion. Should match the way you
......
RocketChat.authz.hasPermission = (userId, permissionId, scope) ->
permission = RocketChat.models.Permissions.findOne permissionId
return RocketChat.models.Roles.isUserInRoles(userId, permission.roles, scope)
atLeastOne = (userId, permissions, scope) ->
return _.some permissions, (permissionId) ->
permission = RocketChat.models.Permissions.findOne permissionId
RocketChat.models.Roles.isUserInRoles(userId, permission.roles, scope)
all = (userId, permissions, scope) ->
return _.every permissions, (permissionId) ->
permission = RocketChat.models.Permissions.findOne permissionId
RocketChat.models.Roles.isUserInRoles(userId, permission.roles, scope)
hasPermission = (userId, permissions, scope, strategy) ->
unless userId
return false
permissions = [].concat permissions
return strategy(userId, permissions, scope)
RocketChat.authz.hasAllPermission = (userId, permissions, scope) ->
return hasPermission(userId, permissions, scope, all)
RocketChat.authz.hasPermission = RocketChat.authz.hasAllPermission
RocketChat.authz.hasAtLeastOnePermission = (userId, permissions, scope) ->
return hasPermission(userId, permissions, scope, atLeastOne)
node_modules
This directory and the files immediately inside it are automatically generated
when you change this package's NPM dependencies. Commit the files in this
directory (npm-shrinkwrap.json, .gitignore, and this README) to source control
so that others run the same versions of sub-dependencies.
You should NOT check in the node_modules directory that Meteor automatically
creates; if you are using git, the .gitignore file tells git to ignore it.
{
"dependencies": {
"atlassian-crowd": {
"version": "0.5.0"
}
}
}
Meteor.loginWithCrowd = function(username, password, callback) {
// Retrieve arguments as array
var args = [];
for (var i = 0; i < arguments.length; i++) {
args.push(arguments[i]);
}
// Pull username and password
username = args.shift();
password = args.shift();
var loginRequest = {
username: username,
crowdPassword: password
};
Accounts.callLoginMethod({
methodArguments: [loginRequest],
userCallback: function(error) {
if (error) {
if (callback) {
callback(error);
}
} else if (callback) {
callback();
}
}
});
};
\ No newline at end of file
Package.describe({
name: 'rocketchat:crowd',
version: '1.0.0',
summary: 'Accounts login handler for crowd using atlassian-crowd-client from npm',
git: ''
});
Package.onUse(function(api) {
api.versionsFrom('1.0');
api.use('rocketchat:logger');
api.use('rocketchat:lib');
api.use('ecmascript');
api.use('sha');
api.use('templating', 'client');
api.use('accounts-base', 'server');
api.use('accounts-password', 'server');
api.addFiles('client/loginHelper.js', 'client');
api.addFiles('server/crowd.js', 'server');
api.addFiles('server/settings.js', 'server');
api.export('CROWD', 'server');
});
Npm.depends({
'atlassian-crowd': '0.5.0'
});
\ No newline at end of file
/* globals:CROWD:true */
/* eslint new-cap: [2, {"capIsNewExceptions": ["SHA256"]}] */
const logger = new Logger('CROWD', {});
function fallbackDefaultAccountSystem(bind, username, password) {
if (typeof username === 'string') {
if (username.indexOf('@') === -1) {
username = {username: username};
} else {
username = {email: username};
}
}
logger.info('Fallback to default account system', username);
const loginRequest = {
user: username,
password: {
digest: SHA256(password),
algorithm: 'sha-256'
}
};
return Accounts._runLoginHandlers(bind, loginRequest);
}
const CROWD = class CROWD {
constructor() {
const AtlassianCrowd = Npm.require('atlassian-crowd');
let url = RocketChat.settings.get('CROWD_URL');
let urlLastChar = url.slice(-1);
if (urlLastChar !== '/') {
url += '/';
}
this.options = {
crowd: {
base: url
},
application: {
name: RocketChat.settings.get('CROWD_APP_USERNAME'),
password: RocketChat.settings.get('CROWD_APP_PASSWORD')
}
};
this.crowdClient = new AtlassianCrowd(this.options);
this.crowdClient.user.authenticateSync = Meteor.wrapAsync(this.crowdClient.user.authenticate, this);
this.crowdClient.user.findSync = Meteor.wrapAsync(this.crowdClient.user.find, this);
this.crowdClient.pingSync = Meteor.wrapAsync(this.crowdClient.ping, this);
}
checkConnection() {
this.crowdClient.pingSync();
}
authenticate(username, password) {
if (!username || !password) {
logger.error('No username or password');
return;
}
logger.info('Going to crowd:', username);
const auth = this.crowdClient.user.authenticateSync(username, password);
if (!auth) {
return;
}
const userResponse = this.crowdClient.user.findSync(username);
const user = {
displayname: userResponse['display-name'],
username: userResponse.name,
email: userResponse.email,
password: password,
active: userResponse.active
};
return user;
}
syncDataToUser(crowdUser, id) {
const user = {
name: crowdUser.displayname,
username: crowdUser.username,
emails: [{
address : crowdUser.email,
verified: true
}],
password: crowdUser.password,
active: crowdUser.active
};
Meteor.users.update(id, {
$set: user
});
}
sync() {
if (RocketChat.settings.get('CROWD_Enable') !== true) {
return;
}
var self = this;
logger.info('Sync started');
const users = RocketChat.models.Users.findCrowdUsers();
if (users) {
users.forEach(function(user) {
logger.info('Syncing user', user.username);
const userResponse = self.crowdClient.user.findSync(user.username);
if (userResponse) {
const crowdUser = {
displayname: userResponse['display-name'],
username: userResponse.name,
email: userResponse.email,
password: userResponse.password,
active: userResponse.active
};
self.syncDataToUser(crowdUser, user._id);
}
});
}
}
addNewUser(crowdUser) {
var userQuery = {
crowd: true,
username: crowdUser.username
};
// find our existinmg user if they exist
const user = Meteor.users.findOne(userQuery);
if (user) {
const stampedToken = Accounts._generateStampedLoginToken();
Meteor.users.update(user._id, {
$push: {
'services.resume.loginTokens': Accounts._hashStampedToken(stampedToken)
}
});
this.syncDataToUser(crowdUser, user._id);
return {
userId: user._id,
token: stampedToken.token
};
} else {
try {
crowdUser._id = Accounts.createUser(crowdUser);
} catch (error) {
logger.info('Error creating new user for crowd user', error);
}
const updateUser = {
name: crowdUser.displayname,
crowd: true,
active: crowdUser.active
};
Meteor.users.update(crowdUser._id, {
$set: updateUser
});
}
Meteor.runAsUser(crowdUser._id, function() {
Meteor.call('joinDefaultChannels');
});
return {
userId: crowdUser._id
};
}
};
Accounts.registerLoginHandler('crowd', function(loginRequest) {
const crowd = new CROWD();
let user;
try {
user = crowd.authenticate(loginRequest.username, loginRequest.crowdPassword);
} catch (error) {
logger.error('Crowd user not authenticated due to an error, falling back');
}
if (!user) {
return fallbackDefaultAccountSystem(this, loginRequest.username, loginRequest.crowdPassword);
}
return crowd.addNewUser(user);
});
let interval;
let timeout;
RocketChat.settings.get('CROWD_Sync_User_Data', function(key, value) {
Meteor.clearInterval(interval);
Meteor.clearTimeout(timeout);
if (value === true) {
const crowd = new CROWD();
logger.info('Enabling CROWD user sync');
Meteor.setInterval(crowd.sync, 1000 * 60 * 60);
Meteor.setTimeout(function() {
crowd.sync();
}, 1000 * 30);
} else {
logger.info('Disabling CROWD user sync');
}
});
Meteor.methods({
crowd_test_connection:function() {
const user = Meteor.user();
if (!user) {
throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'crowd_test_connection' });
}
if (!RocketChat.authz.hasRole(user._id, 'admin')) {
throw new Meteor.Error('error-not-authorized', 'Not authorized', { method: 'crowd_test_connection' });
}
if (RocketChat.settings.get('CROWD_Enable') !== true) {
throw new Meteor.Error('crowd_disabled');
}
let crowd = new CROWD();
try {
crowd.checkConnection();
} catch (error) {
logger.error('Invalid crowd connection details, check the url and application username/password and make sure this server is allowed to speak to crowd');
throw new Meteor.Error('Invalid connection details', '', { method: 'crowd_test_connection' });
}
return {
message: 'Connection success',
params: []
};
}
});
Meteor.startup(function() {
RocketChat.settings.addGroup('Atlassian Crowd', function() {
const enableQuery = {_id: 'CROWD_Enable', value: true};
this.add('CROWD_Enable', false, { type: 'boolean', public: true, i18nLabel: 'Enabled' });
this.add('CROWD_URL', '', { type: 'string', enableQuery: enableQuery, i18nLabel: 'URL' });
this.add('CROWD_APP_USERNAME', '', { type: 'string', enableQuery: enableQuery, i18nLabel: 'Username' });
this.add('CROWD_APP_PASSWORD', '', { type: 'string', enableQuery: enableQuery, i18nLabel: 'Password' });
this.add('CROWD_Sync_User_Data', false, { type: 'boolean', enableQuery: enableQuery, i18nLabel: 'Sync_Users' });
this.add('CROWD_Test_Connection', 'crowd_test_connection', { type: 'action', actionText: 'Test_Connection', i18nLabel: 'Test_Connection' });
});
});
node_modules
This directory and the files immediately inside it are automatically generated
when you change this package's NPM dependencies. Commit the files in this
directory (npm-shrinkwrap.json, .gitignore, and this README) to source control
so that others run the same versions of sub-dependencies.
You should NOT check in the node_modules directory that Meteor automatically
creates; if you are using git, the .gitignore file tells git to ignore it.
{
"dependencies": {
"mime-types": {
"version": "2.1.11",
"dependencies": {
"mime-db": {
"version": "1.23.0"
}
}
}
}
}
......@@ -46,3 +46,7 @@ Package.onUse(function(api) {
api.export('fileUploadHandler');
api.export('FileUpload');
});
Npm.depends({
'mime-types': '2.1.11'
});
......@@ -55,7 +55,8 @@ FileUpload.addHandler(storeName, {
let stat = Meteor.wrapAsync(fs.stat)(filePath);
if (stat && stat.isFile()) {
res.setHeader('Content-Disposition', 'attachment; filename="' + encodeURIComponent(file.name) + '"');
file = FileUpload.addExtensionTo(file);
res.setHeader('Content-Disposition', `attachment; filename*=UTF-8''${encodeURIComponent(file.name)}`);
res.setHeader('Last-Modified', file.uploadedAt.toUTCString());
res.setHeader('Content-Type', file.type);
res.setHeader('Content-Length', file.size);
......
......@@ -47,8 +47,9 @@ var readFromGridFS = function(storeName, fileId, file, headers, req, res) {
FileUpload.addHandler('rocketchat_uploads', {
get(file, req, res) {
file = FileUpload.addExtensionTo(file);
let headers = {
'Content-Disposition': 'attachment; filename="' + encodeURIComponent(file.name) + '"',
'Content-Disposition': `attachment; filename*=UTF-8''${encodeURIComponent(file.name)}`,
'Last-Modified': file.uploadedAt.toUTCString(),
'Content-Type': file.type,
'Content-Length': file.size
......
/* globals FileUpload:true */
const mime = Npm.require('mime-types');
FileUpload.handlers = {};
FileUpload.addHandler = function(store, handler) {
......@@ -26,3 +28,16 @@ FileUpload.get = function(file, req, res, next) {
return;
}
};
FileUpload.addExtensionTo = function(file) {
if (mime.lookup(file.name) === file.type) {
return file;
}
const ext = mime.extension(file.type);
if (ext && false === new RegExp(`\.${ext}$`, 'i').test(file.name)) {
file.name = `${file.name}.${ext}`;
}
return file;
};
......@@ -9,7 +9,7 @@ RocketChat.settings.addGroup('FileUpload', function() {
public: true
});
this.add('FileUpload_MediaTypeWhiteList', 'image/*,audio/*,application/pdf,text/plain,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document', {
this.add('FileUpload_MediaTypeWhiteList', 'image/*,audio/*,video/*,application/zip,application/x-rar-compressed,application/pdf,text/plain,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document', {
type: 'string',
public: true,
i18nDescription: 'FileUpload_MediaTypeWhiteListDescription'
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment