Skip to content

Commit

Permalink
Close #2378 Add role tags (#2858)
Browse files Browse the repository at this point in the history
* Room Roles

* Replace arunoda:streams by rocketchat:streamer

* Getting user roles

* Add stream notifications for adding/removing user and room roles

* Fix event broadcast

* Update version of rocketchat:streamer to 0.2.0

* Call subscribe after adding user to role

* Update rocketchat:streamer to version 0.3.0

* Notify broadcast

* Notify and broadcast as default

* Update rocketchat:streamer to version 0.3.1

* Update rocketchat:streamer to 0.3.2

* Improve broadcast auth

* Fix LiveChat Streamer
  • Loading branch information
marceloschmidt authored and engelgabriel committed Apr 13, 2016
1 parent 43efe6d commit 4d89802
Show file tree
Hide file tree
Showing 32 changed files with 233 additions and 153 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -83,15 +83,17 @@ Template.permissionsRole.events
description: e.currentTarget.elements['description'].value
scope: e.currentTarget.elements['scope'].value

if not @_id?
if @_id
roleData.name = @_id
else
roleData.name = e.currentTarget.elements['name'].value

Meteor.call 'authorization:saveRole', @_id, roleData, (error, result) =>

Meteor.call 'authorization:saveRole', roleData, (error, result) =>
e.currentTarget.elements['save'].value = oldBtnValue
if error
return toastr.error t(error.reason || error.error)

e.currentTarget.reset()
toastr.success t('Saved')

if not @_id?
Expand All @@ -108,11 +110,12 @@ Template.permissionsRole.events

e.currentTarget.elements['add'].value = t('Saving')

Meteor.call 'authorization:addUserToRole', FlowRouter.getParam('name'), e.currentTarget.elements['username'].value, instance.searchRoom.get(), (error, result) ->
Meteor.call 'authorization:addUserToRole', FlowRouter.getParam('name'), e.currentTarget.elements['username'].value, instance.searchRoom.get(), (error, result) =>
e.currentTarget.elements['add'].value = oldBtnValue
if error
return toastr.error t(error.reason || error.error)

instance.subscribe 'usersInRole', FlowRouter.getParam('name'), instance.searchRoom.get()
toastr.success t('User_added')
e.currentTarget.reset()

Expand Down Expand Up @@ -141,9 +144,10 @@ Template.permissionsRole.onCreated ->
@usersInRole = new ReactiveVar

@subscribe 'roles', FlowRouter.getParam('name')
@subscribe 'usersInRole', FlowRouter.getParam('name')

@autorun =>
if @searchRoom.get()
@subscribe 'roomSubscriptionsByRole', @searchRoom.get(), FlowRouter.getParam('name')

@subscribe 'usersInRole', FlowRouter.getParam('name'), @searchRoom.get()
@usersInRole.set(RocketChat.models.Roles.findUsersInRole(FlowRouter.getParam('name'), @searchRoom.get(), { sort: { username: 1 } }))
76 changes: 40 additions & 36 deletions packages/rocketchat-authorization/client/views/permissionsRole.html
Original file line number Diff line number Diff line change
Expand Up @@ -40,44 +40,48 @@ <h2>{{_ "Users_in_role"}}</h2>
</form>
{{/if}}
{{#if $or ($eq role.scope 'Users') searchRoom}}
<form id="form-users" class="inline">
<label>{{_ "Add_user"}}</label>
<input type="text" name="username" placeholder="{{_ "Enter_a_username"}}">
<button name="add" class="button primary">{{_ "Add"}}</button>
</form>
<div class="list">
<table>
<thead>
<tr>
<th>&nbsp;</th>
<th width="34%">{{_ "Name"}}</th>
<th width="33%">{{_ "Username"}}</th>
<th width="33%">{{_ "E-mail"}}</th>
<th>&nbsp;</th>
</tr>
</thead>
<tbody>
{{#unless hasUsers}}
{{#if Template.subscriptionsReady}}
<form id="form-users" class="inline">
<label>{{_ "Add_user"}}</label>
<input type="text" name="username" placeholder="{{_ "Enter_a_username"}}">
<button name="add" class="button primary">{{_ "Add"}}</button>
</form>
<div class="list">
<table>
<thead>
<tr>
<td colspan="5" class="empty-role">{{_ "There_are_no_users_in_this_role"}}</td>
<th>&nbsp;</th>
<th width="34%">{{_ "Name"}}</th>
<th width="33%">{{_ "Username"}}</th>
<th width="33%">{{_ "E-mail"}}</th>
<th>&nbsp;</th>
</tr>
{{/unless}}
{{#each userInRole}}
<tr class="user-info" data-id="{{_id}}">
<td>
<div class="user-image status-{{status}}">
{{> avatar username=username}}
</div>
</td>
<td>{{name}}</td>
<td>{{username}}</td>
<td>{{emailAddress}}</td>
<td><a href="#remove" class="remove-user"><i class="icon-block"></i></a></td>
</tr>
{{/each}}
</tbody>
</table>
</div>
</thead>
<tbody>
{{#unless hasUsers}}
<tr>
<td colspan="5" class="empty-role">{{_ "There_are_no_users_in_this_role"}}</td>
</tr>
{{/unless}}
{{#each userInRole}}
<tr class="user-info" data-id="{{_id}}">
<td>
<div class="user-image status-{{status}}">
{{> avatar username=username}}
</div>
</td>
<td>{{name}}</td>
<td>{{username}}</td>
<td>{{emailAddress}}</td>
<td><a href="#remove" class="remove-user"><i class="icon-block"></i></a></td>
</tr>
{{/each}}
</tbody>
</table>
</div>
{{else}}
{{_ "Loading..."}}
{{/if}}
{{/if}}
{{/if}}
{{else}}
Expand Down
1 change: 1 addition & 0 deletions packages/rocketchat-authorization/package.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ Package.onUse(function(api) {
api.use('templating', 'client');

api.addFiles('lib/rocketchat.coffee', ['server','client']);

api.addFiles('client/lib/ChatPermissions.coffee', ['client']);
api.addFiles('client/lib/models/Roles.coffee', ['client']);
api.addFiles('client/lib/models/Users.js', ['client']);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,6 @@ Meteor.methods
if not user?._id?
throw new Meteor.Error 'user-not-found', 'User_not_found'

RocketChat.Notifications.notifyAll('roles-change', { type: 'added', _id: roleName, u: { _id: user._id, username: username }, scope: scope });

return RocketChat.models.Roles.addUserRoles user._id, roleName, scope
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,6 @@ Meteor.methods
if not user?._id?
throw new Meteor.Error 'user-not-found'

RocketChat.Notifications.notifyAll('roles-change', { type: 'removed', _id: roleName, u: { _id: user._id, username: username }, scope: scope });

return RocketChat.models.Roles.removeUserRoles user._id, roleName, scope
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
Meteor.methods
'authorization:saveRole': (_id, roleData) ->
'authorization:saveRole': (roleData) ->
if not Meteor.userId() or not RocketChat.authz.hasPermission Meteor.userId(), 'access-permissions'
throw new Meteor.Error "error-not-authorized", 'Not authorized', { method: 'authorization:saveRole' }

Expand All @@ -9,4 +9,6 @@ Meteor.methods
if roleData.scope not in ['Users', 'Subscriptions']
roleData.scope = 'Users'

RocketChat.Notifications.notifyAll('roles-change', { type: 'changed', _id: roleData.name });

return RocketChat.models.Roles.createOrUpdate roleData.name, roleData.scope, roleData.description
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Meteor.publish 'usersInRole', (roleName, page = 1) ->
Meteor.publish 'usersInRole', (roleName, scope, page = 1) ->
unless @userId
return @ready()

Expand All @@ -12,4 +12,4 @@ Meteor.publish 'usersInRole', (roleName, page = 1) ->
limit: itemsPerPage
offset: itemsPerPage * (page - 1)

return RocketChat.authz.getUsersInRole roleName, null, pagination
return RocketChat.authz.getUsersInRole roleName, scope, pagination
10 changes: 5 additions & 5 deletions packages/rocketchat-authorization/server/startup.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,11 @@ Meteor.startup ->
RocketChat.models.Permissions.upsert( permission._id, {$set: permission })

defaultRoles = [
{ name: 'admin', scope: 'Users', description: 'Rocket.Chat admins' }
{ name: 'moderator', scope: 'Subscriptions', description: 'Room moderators' }
{ name: 'owner', scope: 'Subscriptions', description: 'Room owners' }
{ name: 'user', scope: 'Users', description: 'Users' }
{ name: 'bot', scope: 'Users', description: 'Bots' }
{ name: 'admin', scope: 'Users', description: 'Admin' }
{ name: 'moderator', scope: 'Subscriptions', description: 'Moderator' }
{ name: 'owner', scope: 'Subscriptions', description: 'Owner' }
{ name: 'user', scope: 'Users', description: '' }
{ name: 'bot', scope: 'Users', description: '' }
]

for role in defaultRoles
Expand Down
37 changes: 37 additions & 0 deletions packages/rocketchat-lib/client/lib/userRoles.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/* globals UserRoles, RoomRoles */

Meteor.startup(function() {
Tracker.autorun(function() {
if (Meteor.userId()) {
Meteor.call('getUserRoles', (error, results) => {
if (error) {
return toastr.error(TAPi18n.__(error.error));
}

for (let record of results) {
UserRoles.upsert({ _id: record._id }, record);
}
});

RocketChat.Notifications.onAll('roles-change', function(role) {
if (role.type === 'added') {
if (role.scope) {
RoomRoles.upsert({ rid: role.scope, 'u._id': role.u._id }, { $setOnInsert: { u: role.u }, $addToSet: { roles: role._id } });
} else {
UserRoles.upsert({ _id: role.u._id }, { $addToSet: { roles: role._id }, $set: { username: role.u.username } });
ChatMessage.update({ 'u._id': role.u._id }, { $addToSet: { roles: role._id } }, { multi: true });
}
} else if (role.type === 'removed') {
if (role.scope) {
RoomRoles.update({ rid: role.scope, 'u._id': role.u._id }, { $pull: { roles: role._id } });
} else {
UserRoles.update({ _id: role.u._id }, { $pull: { roles: role._id } });
ChatMessage.update({ 'u._id': role.u._id }, { $pull: { roles: role._id } }, { multi: true });
}
} else if (role.type === 'changed') {
ChatMessage.update({ roles: role._id }, { $inc: { rerender: 1 } }, { multi: true });
}
});
}
});
});
12 changes: 5 additions & 7 deletions packages/rocketchat-lib/i18n/en.i18n.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
"1_Errors_and_Information" : "1 - Errors and Information",
"2_Erros_Information_and_Debug" : "2 - Erros, Information and Debug",
"500" : "Internal Server Error",
"__username__was_set__role__by__user_by_" : "__username__ was set __role__ by __user_by__",
"__username__is_no_longer__role__defined_by__user_by_" : "__username__ is no longer __role__, by __user_by__",
"Access_not_authorized" : "Access not authorized",
"Access_online_demo" : "Access the online demo",
"Access_Online_Demo" : "Access the Online Demo",
Expand All @@ -11,8 +13,6 @@
"Accounts_AllowDeleteOwnAccount" : "Allow users to delete own account",
"Accounts_AllowedDomainsList" : "Allowed Domains List",
"Accounts_AllowedDomainsList_Description" : "Comma-separated list of allowed domains",
"Accounts_BlockedDomainsList" : "Blocked Domains List",
"Accounts_BlockedDomainsList_Description" : "Comma-separated list of blocked domains",
"Accounts_AllowEmailChange" : "Allow E-mail Change",
"Accounts_AllowPasswordChange" : "Allow Password Change",
"Accounts_AllowUserAvatarChange" : "Allow User Avatar Change",
Expand All @@ -22,8 +22,9 @@
"Accounts_AvatarSize" : "Avatar Size",
"Accounts_AvatarStorePath" : "Avatar Storage Path",
"Accounts_AvatarStoreType" : "Avatar Storage Type",
"Accounts_BlockedDomainsList" : "Blocked Domains List",
"Accounts_BlockedDomainsList_Description" : "Comma-separated list of blocked domains",
"Accounts_denyUnverifiedEmail" : "Deny unverified email",
"Accounts_UseDNSDomainCheck" : "Use DNS Domain Check",
"Accounts_EmailVerification" : "E-mail Verification",
"Accounts_EmailVerification_Description" : "Make sure you have correct SMTP settings to use this feature",
"Accounts_Enrollment_Email" : "Enrollment E-mail",
Expand Down Expand Up @@ -95,6 +96,7 @@
"Accounts_RequireNameForSignUp" : "Require Name For Signup",
"Accounts_ShowFormLogin" : "Show form-based Login",
"Accounts_UseDefaultBlockedDomainsList" : "Use Default Blocked Domains List",
"Accounts_UseDNSDomainCheck" : "Use DNS Domain Check",
"Activate" : "Activate",
"Activity" : "Activity",
"Add" : "Add",
Expand Down Expand Up @@ -963,10 +965,6 @@
"User__username__is_now_a_owner_of__room_name_" : "User __username__ is now a owner of __room_name__",
"User__username__removed_from__room_name__moderators" : "User __username__ removed from __room_name__ moderators",
"User__username__removed_from__room_name__owners" : "User __username__ removed from __room_name__ owners",
"User__username__was_added_as_a_moderator_by__user_by_" : "User <em>__username__</em> was added as a moderator by <em>__user_by__</em>",
"User__username__was_added_as_a_owner_by__user_by_" : "User <em>__username__</em> was added as a owner by <em>__user_by__</em>",
"User__username__was_removed_as_a_moderator_by__user_by_" : "User <em>__username__</em> was removed as a moderator by <em>__user_by__</em>",
"User__username__was_removed_as_a_owner_by__user_by_" : "User <em>__username__</em> was removed as a owner by <em>__user_by__</em>",
"User_added" : "User added",
"User_added_by" : "User <em>__user_added__</em> added by <em>__user_by__</em>.",
"User_added_successfully" : "User added successfully",
Expand Down
24 changes: 6 additions & 18 deletions packages/rocketchat-lib/lib/MessageTypes.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -84,29 +84,17 @@ Meteor.startup ->
return { user_unmuted: message.msg, user_by: message.u.username }

RocketChat.MessageTypes.registerType
id: 'new-moderator'
id: 'subscription-role-added'
system: true
message: 'User__username__was_added_as_a_moderator_by__user_by_'
message: '__username__was_set__role__by__user_by_'
data: (message) ->
return { username: message.msg, user_by: message.u.username }
return { username: message.msg, role: message.role, user_by: message.u.username }

RocketChat.MessageTypes.registerType
id: 'moderator-removed'
id: 'subscription-role-removed'
system: true
message: 'User__username__was_removed_as_a_moderator_by__user_by_'
message: '__username__is_no_longer__role__defined_by__user_by_'
data: (message) ->
return { username: message.msg, user_by: message.u.username }
return { username: message.msg, role: message.role, user_by: message.u.username }

RocketChat.MessageTypes.registerType
id: 'new-owner'
system: true
message: 'User__username__was_added_as_a_owner_by__user_by_'
data: (message) ->
return { username: message.msg, user_by: message.u.username }

RocketChat.MessageTypes.registerType
id: 'owner-removed'
system: true
message: 'User__username__was_removed_as_a_owner_by__user_by_'
data: (message) ->
return { username: message.msg, user_by: message.u.username }
3 changes: 3 additions & 0 deletions packages/rocketchat-lib/package.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ Package.onUse(function(api) {
api.addFiles('server/methods/addOAuthService.coffee', 'server');
api.addFiles('server/methods/checkRegistrationSecretURL.coffee', 'server');
api.addFiles('server/methods/deleteUserOwnAccount.js', 'server');
api.addFiles('server/methods/getRoomRoles.js', 'server');
api.addFiles('server/methods/getUserRoles.js', 'server');
api.addFiles('server/methods/joinDefaultChannels.coffee', 'server');
api.addFiles('server/methods/removeOAuthService.coffee', 'server');
api.addFiles('server/methods/robotMethods.coffee', 'server');
Expand Down Expand Up @@ -110,6 +112,7 @@ Package.onUse(function(api) {
api.addFiles('client/lib/roomExit.coffee', 'client');
api.addFiles('client/lib/settings.coffee', 'client');
api.addFiles('client/lib/roomTypes.coffee', 'client');
api.addFiles('client/lib/userRoles.js', 'client');

// CLIENT METHODS
api.addFiles('client/methods/sendMessage.coffee', 'client');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ RocketChat.Notifications = new class


notifyAllInThisInstance: (eventName, args...) ->
console.log 'notifyAllAndBroadcast', arguments if @debug is true
console.log 'notifyAll', arguments if @debug is true

args.unshift eventName
@streamAll.emitWithoutBroadcast.apply @streamAll, args
Expand Down
23 changes: 23 additions & 0 deletions packages/rocketchat-lib/server/methods/getRoomRoles.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
Meteor.methods({
getRoomRoles(rid) {
if (!Meteor.userId()) {
throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'getRoomRoles' });
}

check(rid, String);

const options = {
sort: {
'u.username': 1
},
fields: {
rid: 1,
u: 1,
roles: 1
}
};

const roles = RocketChat.models.Roles.find({ scope: 'Subscriptions', description: { $exists: 1, $ne: '' } }).fetch();
return RocketChat.models.Subscriptions.findByRoomIdAndRoles(rid, _.pluck(roles, '_id'), options).fetch();
}
});
28 changes: 28 additions & 0 deletions packages/rocketchat-lib/server/methods/getUserRoles.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
Meteor.methods({
getUserRoles() {
if (!Meteor.userId()) {
throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'getUserRoles' });
}

const options = {
sort: {
'username': 1
},
fields: {
username: 1,
roles: 1
}
};

const roles = RocketChat.models.Roles.find({ scope: 'Users', description: { $exists: 1, $ne: '' } }).fetch();
const roleIds = _.pluck(roles, '_id');

// Security issue: we should not send all user's roles to all clients, only the 'public' roles
// We must remove all roles that are not part of the query from the returned users
let users = RocketChat.models.Users.findUsersInRoles(roleIds, null, options).fetch();
for (let user of users) {
user.roles = _.intersection(user.roles, roleIds);
}
return users;
}
});
Loading

0 comments on commit 4d89802

Please sign in to comment.