diff --git a/.docker/dockerfiles/develop/Dockerfile b/.docker/dockerfiles/develop/Dockerfile index bbce2753d91daf01b6b15a8e2c6df8c2e4eb93ef..5707cfbc01710ce9ad5467f50802aefeabc3ee10 100644 --- a/.docker/dockerfiles/develop/Dockerfile +++ b/.docker/dockerfiles/develop/Dockerfile @@ -2,10 +2,6 @@ FROM node:0.10 MAINTAINER buildmaster@rocket.chat -RUN apt-get update \ -&& apt-get install -y graphicsmagick \ -&& rm -rf /var/lib/apt/lists/* - RUN groupadd -r rocketchat \ && useradd -r -g rocketchat rocketchat \ && mkdir /app \ @@ -16,7 +12,7 @@ RUN gpg --keyserver ha.pool.sks-keyservers.net --recv-keys 0E163286C20D07B9787EB WORKDIR /app -RUN curl -fSL "https://s3.amazonaws.com/build/rocket.chat-develop.tgz" -o rocket.chat.tgz \ +RUN curl -fSL https://rocket.chat/releases/develop/download -o rocket.chat.tgz \ && tar zxvf ./rocket.chat.tgz \ && rm ./rocket.chat.tgz \ && cd /app/bundle/programs/server \ diff --git a/.docker/dockerfiles/latest/Dockerfile b/.docker/dockerfiles/latest/Dockerfile index 296f2e76dc47f3b3ded90bedb37a988abd7b5292..ed09ceb5bf895a4b96a451f44706d635a79503de 100644 --- a/.docker/dockerfiles/latest/Dockerfile +++ b/.docker/dockerfiles/latest/Dockerfile @@ -12,10 +12,7 @@ RUN gpg --keyserver ha.pool.sks-keyservers.net --recv-keys 0E163286C20D07B9787EB WORKDIR /app -RUN URL="https://builds.rocket.chat/releases/latest" \ -&& FILE="/rocket.chat.tgz" \ -&& HEADER=$(curl -I -s "$URL" | grep -Fi Location: | sed -En 's/.*(https?:\/\/[a-zA-Z0-9\/.-_]*).*$/\1/p' | sed 's/\/tag\//\/download\//' ) \ -&& curl -fSL "$HEADER$FILE" -o rocket.chat.tgz \ +RUN curl -fSL https://rocket.chat/releases/latest/download -o rocket.chat.tgz \ && tar zxvf ./rocket.chat.tgz \ && rm ./rocket.chat.tgz \ && cd /app/bundle/programs/server \ diff --git a/.meteor/versions b/.meteor/versions index a894cf14bf30a7604d1346a0a653af74c4663071..18a8a4d48ade916b58a45db71e468a26d6f8dfc5 100644 --- a/.meteor/versions +++ b/.meteor/versions @@ -6,7 +6,6 @@ accounts-meteor-developer@1.0.6 accounts-oauth@1.1.8 accounts-password@1.1.4 accounts-twitter@1.0.6 -alanning:roles@1.2.14 aldeed:simple-schema@1.5.3 arunoda:streams@0.1.17 autoupdate@1.2.4 @@ -148,7 +147,7 @@ rocketchat:mentions-flextab@0.0.1 rocketchat:message-attachments@0.0.1 rocketchat:message-pin@0.0.1 rocketchat:message-star@0.0.1 -rocketchat:oauth2-server@1.1.1 +rocketchat:oauth2-server@1.2.0 rocketchat:oauth2-server-config@1.0.0 rocketchat:oembed@0.0.1 rocketchat:slashcommands-invite@0.0.1 diff --git a/.travis/docker.sh b/.travis/docker.sh index 84eb7f7463d50a4fe0197d55ff43dacf5ead8314..1982a9ef008e439d7d7a6ffc47c68ee718f1f824 100755 --- a/.travis/docker.sh +++ b/.travis/docker.sh @@ -7,7 +7,7 @@ CURL_URL="https://registry.hub.docker.com/u/rocketchat/rocket.chat/trigger/$PUSH if [[ -v "$TRAVIS_TAG" ]]; then CURL_DATA='{"source_type":"Tag","source_name":"'"$TRAVIS_TAG"'"}'; else - CURL_DATA='{"docker_tag":"develop"}'; + CURL_DATA='{"source_type":"Branch","source_name":"develop"}'; fi curl -H "Content-Type: application/json" --data "$CURL_DATA" -X POST "$CURL_URL" diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 72c9a4ea913eb89a7389de95601e7d297e894221..0000000000000000000000000000000000000000 --- a/Dockerfile +++ /dev/null @@ -1,36 +0,0 @@ -FROM node:0.10 - -MAINTAINER buildmaster@rocket.chat - -RUN groupadd -r rocketchat \ -&& useradd -r -g rocketchat rocketchat \ -&& mkdir /app \ -&& mkdir /app/uploads - -# gpg: key 4FD08014: public key "Rocket.Chat Buildmaster <buildmaster@rocket.chat>" imported -RUN gpg --keyserver ha.pool.sks-keyservers.net --recv-keys 0E163286C20D07B9787EBE9FD7F9D0414FD08104 - -WORKDIR /app - -RUN URL="https://github.com/RocketChat/Rocket.Chat/releases/latest" \ -&& FILE="/rocket.chat.tgz" \ -&& HEADER=$(curl -I -s "$URL" | grep -Fi Location: | sed -En 's/.*(https?:\/\/[a-zA-Z0-9\/.-_]*).*$/\1/p' | sed 's/\/tag\//\/download\//' ) \ -&& curl -fSL "$HEADER$FILE" -o rocket.chat.tgz \ -&& tar zxvf ./rocket.chat.tgz \ -&& rm ./rocket.chat.tgz \ -&& cd /app/bundle/programs/server \ -&& npm install - -USER rocketchat - -VOLUME /app/uploads -WORKDIR /app/bundle - -# needs a mongoinstance - defaults to container linking with alias 'mongo' -ENV MONGO_URL=mongodb://mongo:27017/rocketchat \ - PORT=3000 \ - ROOT_URL=http://localhost:3000 \ - Accounts_AvatarStorePath=/app/uploads - -EXPOSE 3000 -CMD ["node", "main.js"] diff --git a/HISTORY.md b/HISTORY.md index 625dae07616436eec88a0ce6d7775b6eb7930b60..a1e5feccfb9f774d5b501465e11cd9ce0b3cac93 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,7 +1,12 @@ -## v.NEXT +## NEXT +- -## v0.12.0, 2016-Jan-04 +## 0.12.1, 2016-Jan-05 + +- Fix problem with middleware that tries to parse json body + +## 0.12.0, 2016-Jan-04 - Settings: unset section if none is given on update - Hide registration and forgot password links when hidding login form @@ -19,7 +24,7 @@ - Try to parse all request bodies as JSON - New password reset screen -## v0.11.0, 2015-Dec-28 +## 0.11.0, 2015-Dec-28 - Add role bot to users of integrations in scope bot - Add route to cadastre new integrations via API @@ -62,15 +67,15 @@ - Turn channel and triggerWords optional in triggers - Using branding image from main APP -## v0.10.2, 2015-Dec-22 +## 0.10.2, 2015-Dec-22 - Fixes image preview bugs with filenames containing spaces -## v0.10.1, 2015-Dec-21 +## 0.10.1, 2015-Dec-21 - Fix upload permissions introduced in raik:ufs 0.3.4 -## v0.10.0, 2015-Dec-21 +## 0.10.0, 2015-Dec-21 - Accept property *msg* as text in attachments - Add "Room has been deleted" entry @@ -170,7 +175,7 @@ - Use attachments to render preview of uploads and use relative paths - Using flow-router group routes -## v0.9.0, 2015-Dec-14 +## 0.9.0, 2015-Dec-14 - Fix broken image-link when og:image contains "&" (e.g. Google Maps) - Error message when file upload media type it not accepted @@ -212,7 +217,7 @@ - Fixed blockquote non-continous border - Moved accountBox HTML to new separated template -## v0.8.0, 2015-Dec-8 +## 0.8.0, 2015-Dec-8 - Fixed error: when allow change username was set to false, registration - Improve message rendering removing MessageAction from render time @@ -243,6 +248,6 @@ - Clear iOS app badge on app startup - Fix for image swipebox to show in RTL interface -## v0.1.0, 2015-May-19 +## 0.1.0, 2015-May-19 - Initial public launch diff --git a/client/startup/startup.coffee b/client/startup/startup.coffee index f7043af49469bccef656de087027c9970acd8a41..ed209ee71b3bc758fac84fb479c923fe394c2a1e 100644 --- a/client/startup/startup.coffee +++ b/client/startup/startup.coffee @@ -24,6 +24,9 @@ Meteor.startup -> loadedLaguages = [] @setLanguage = (language) -> + if !language + return + if loadedLaguages.indexOf(language) > -1 return diff --git a/docker-compose.yml b/docker-compose.yml index 33a72bd0126ab9835c1d98fb8b07fdb1c7dc6ba1..2637c3c86a24cae08c2ba61cee9b63c0a86d2d5b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -13,8 +13,6 @@ rocketchat: - PORT=3000 - ROOT_URL=http://yourhost:3000 - MONGO_URL=mongodb://mongo:27017/rocketchat -# uncomment and set the line below if you need to support mobile apps -# - DDP_DEFAULT_CONNECTION_URL=http://yourhost:3000 links: - mongo:mongo ports: @@ -24,7 +22,7 @@ rocketchat: hubot: image: rocketchat/hubot-rocketchat environment: - - ROCKETCHAT_URL=yourhost:3000 + - ROCKETCHAT_URL=rocketchat:3000 - ROCKETCHAT_ROOM=GENERAL - ROCKETCHAT_USER=bot - ROCKETCHAT_PASSWORD=botpassword diff --git a/i18n/en.i18n.json b/i18n/en.i18n.json index 6c2d6a8f75653941ae9800c116262b4c895a2b77..482eea38ba63c201b36a90bd8004bef036c8e6ca 100644 --- a/i18n/en.i18n.json +++ b/i18n/en.i18n.json @@ -2,6 +2,7 @@ "Access_not_authorized" : "Access not authorized", "Access_online_demo" : "Access the online demo", "Access_Online_Demo" : "Access the Online Demo", + "Access_Token_URL" : "Access Token URL", "Accounts" : "Accounts", "Accounts_AllowedDomainsList" : "Allowed Domains List", "Accounts_AllowedDomainsList_Description" : "Comma-separated list of allowed domains", @@ -86,6 +87,7 @@ "are_also_typing" : "are also typing", "are_typing" : "are typing", "Are_you_sure" : "Are you sure?", + "Authorization_URL" : "Authorization URL", "Auto_Load_Images" : "Auto Load Images", "Avatar_changed_successfully" : "Avatar changed successfully", "Avatar_URL" : "Avatar URL", diff --git a/packages/rocketchat-authorization/README.md b/packages/rocketchat-authorization/README.md index 239b085c856dc0d1280af7d604b5f5a40c2d2eb7..c2365d83f6986f4c0c0b0889aa1f4c9b147942c3 100644 --- a/packages/rocketchat-authorization/README.md +++ b/packages/rocketchat-authorization/README.md @@ -1,6 +1,6 @@ -Supports role or permission based authorization, and defines the association between them. +Supports role or permission based authorization, and defines the association between them. -A user is associated with role(s), and a role is associated with permission(s). This package depends on alanning:roles for the role/user association, while the role/permission association is handled internally. Thus, the underlying alanning:roles has no concept of a permission or the association between a role and permission. +A user is associated with role(s), and a role is associated with permission(s). This package depends on alanning:roles for the role/user association, while the role/permission association is handled internally. Thus, the underlying alanning:roles has no concept of a permission or the association between a role and permission. Authorization checks can be done based on a role or permission. However, permission based checks are preferred because they loosely associate an action with a role. For example: @@ -19,13 +19,12 @@ if hasRole(userId, ['admin','site-moderator','moderator']) Usage: ``` -# assign user to moderator role. Permissions scoped globally -# user can moderate (e.g. edit channel name, delete private group message) for all rooms -RocketChat.authz.addUsersToRoles(userId, 'moderator') +# assign user to admin role. Permissions scoped globally +RocketChat.authz.addUserRoles(userId, 'admin') # assign user to moderator role. Permissions scoped to the specified room # user can moderate (e.g. edit channel name, delete private group message) for only one room specified by the roomId -RocketChat.authz.addUsersToRoles(userId, 'moderator', roomId ) +RocketChat.authz.addUserRoles(userId, 'moderator', roomId ) # check if user can modify message for any room RocketChat.authz.hasPermission(userId, 'edit-message') @@ -37,5 +36,5 @@ RocketChat.authz.hasPermission(userId, 'edit-message', roomId) Notes: 1. Roles are statically defined. UI needs to be implemented to dynamically assign permission(s) to a Role -2. 'admin', 'moderator', 'user' role identifiers should NOT be changed (unless you update the associated code) because they are referenced when creating users and creating rooms. -3. edit, delete message permissions are at either the global or room scope. i.e. role with edit-message with GLOBAL scope can edit ANY message regardless of the room type. However, role with edit-message with room scope can only edit messages for the room. The global scope is associated with the admin role while the "room-scoped" permission is assigned to the room "moderator" (room creator). If we want a middle ground that allows for edit-message for only channel/group/direct, then we need to create individual edit-c-message, edit-p-message, edit-d-message permissions. +2. 'admin', 'moderator', 'user' role identifiers should NOT be changed (unless you update the associated code) because they are referenced when creating users and creating rooms. +3. edit, delete message permissions are at either the global or room scope. i.e. role with edit-message with GLOBAL scope can edit ANY message regardless of the room type. However, role with edit-message with room scope can only edit messages for the room. The global scope is associated with the admin role while the "room-scoped" permission is assigned to the room "moderator" (room creator). If we want a middle ground that allows for edit-message for only channel/group/direct, then we need to create individual edit-c-message, edit-p-message, edit-d-message permissions. diff --git a/packages/rocketchat-authorization/client/hasPermission.coffee b/packages/rocketchat-authorization/client/hasPermission.coffee index 9d636b9552e7bc47ebb78e80fbb66b60af6a75c1..015ec854ee72935b6bc854010e417cc3f31b1973 100644 --- a/packages/rocketchat-authorization/client/hasPermission.coffee +++ b/packages/rocketchat-authorization/client/hasPermission.coffee @@ -1,25 +1,29 @@ -atLeastOne = (toFind, toSearch) -> - console.log 'toFind: ', toFind if window.rocketDebug - console.log 'toSearch: ', toSearch if window.rocketDebug - return not _.isEmpty(_.intersection(toFind, toSearch)) - -all = (toFind, toSearch) -> - toFind = _.uniq(toFind) - toSearch = _.uniq(toSearch) - return _.isEmpty( _.difference( toFind, toSearch)) +atLeastOne = (permissions, scope) -> + return _.some permissions, (permissionId) -> + permission = ChatPermissions.findOne permissionId + return _.some permission.roles, (roleName) -> + role = RocketChat.models.Roles.findOne roleName + roleScope = role?.scope + return RocketChat.models[roleScope]?.isUserInRole?(Meteor.userId(), roleName, scope) + +all = (permissions, scope) -> + return _.every permissions, (permissionId) -> + permission = ChatPermissions.findOne permissionId + return _.some permission.roles, (roleName) -> + role = RocketChat.models.Roles.findOne roleName + roleScope = role?.scope + return RocketChat.models[roleScope]?.isUserInRole?(Meteor.userId(), roleName, scope) Template.registerHelper 'hasPermission', (permission, scope) -> - unless _.isString( scope ) - scope = Roles.GLOBAL_GROUP - return hasPermission( permission, scope, atLeastOne) + return hasPermission(permission, scope, atLeastOne) -RocketChat.authz.hasAllPermission = (permissions, scope=Roles.GLOBAL_GROUP) -> - return hasPermission( permissions, scope, all ) +RocketChat.authz.hasAllPermission = (permissions, scope) -> + return hasPermission(permissions, scope, all) -RocketChat.authz.hasAtLeastOnePermission = (permissions, scope=Roles.GLOBAL_GROUP) -> +RocketChat.authz.hasAtLeastOnePermission = (permissions, scope) -> return hasPermission(permissions, scope, atLeastOne) -hasPermission = (permissions, scope=Roles.GLOBAL_GROUP, strategy) -> +hasPermission = (permissions, scope, strategy) -> userId = Meteor.userId() unless userId @@ -30,10 +34,4 @@ hasPermission = (permissions, scope=Roles.GLOBAL_GROUP, strategy) -> permissions = [].concat permissions - roleNames = Roles.getRolesForUser(userId, scope) - - userPermissions = [] - for roleName in roleNames - userPermissions = userPermissions.concat(_.pluck(ChatPermissions.find({roles : roleName }).fetch(), '_id')) - - return strategy( permissions, userPermissions) + return strategy(permissions, scope) diff --git a/packages/rocketchat-authorization/client/hasRole.coffee b/packages/rocketchat-authorization/client/hasRole.coffee index 3140a74bfe635cfc05d6ec20b8c59f4eb84f67bd..aff335e48b2fd0f7ae37fce3f2d00bb98796124c 100644 --- a/packages/rocketchat-authorization/client/hasRole.coffee +++ b/packages/rocketchat-authorization/client/hasRole.coffee @@ -1,8 +1,3 @@ -RocketChat.authz.hasRole = (userId, roleName, scope=Roles.GLOBAL_GROUP) -> - unless Meteor.userId() - return false - - roleName = [].concat roleName - - # per alanning:roles, returns true if user is in ANY roles - return Roles.userIsInRole(userId, roleName, scope) +RocketChat.authz.hasRole = (userId, roleNames, scope) -> + roleNames = [].concat roleNames + return RocketChat.models.Roles.isUserInRoles(userId, roleNames, scope) # true if user is in ANY role diff --git a/packages/rocketchat-authorization/client/collection.coffee b/packages/rocketchat-authorization/client/lib/ChatPermissions.coffee similarity index 96% rename from packages/rocketchat-authorization/client/collection.coffee rename to packages/rocketchat-authorization/client/lib/ChatPermissions.coffee index b719f7f9ed648c4d5df0d16de2fd541787017d97..e9adaae10f2f65de5c9d31f3283895af25ec4649 100644 --- a/packages/rocketchat-authorization/client/collection.coffee +++ b/packages/rocketchat-authorization/client/lib/ChatPermissions.coffee @@ -1 +1 @@ -@ChatPermissions = new Meteor.Collection 'rocketchat_permissions' \ No newline at end of file +@ChatPermissions = new Meteor.Collection 'rocketchat_permissions' diff --git a/packages/rocketchat-authorization/client/lib/models/Roles.coffee b/packages/rocketchat-authorization/client/lib/models/Roles.coffee new file mode 100644 index 0000000000000000000000000000000000000000..5d94d584c613bbc6d950efde4d3c28314197c5e9 --- /dev/null +++ b/packages/rocketchat-authorization/client/lib/models/Roles.coffee @@ -0,0 +1,14 @@ +RocketChat.models.Roles = new Meteor.Collection 'rocketchat_roles' + +RocketChat.models.Roles.findUsersInRole = (name, scope, options) -> + role = @findOne name + roleScope = role?.scope or 'Users' + RocketChat.models[roleScope]?.findUsersInRoles?(name, scope, options) + +RocketChat.models.Roles.isUserInRoles = (userId, roles, scope) -> + roles = [].concat roles + _.some roles, (roleName) => + role = @findOne roleName + roleScope = role?.scope or 'Users' + return RocketChat.models[roleScope]?.isUserInRole?(userId, roleName, scope) + diff --git a/packages/rocketchat-authorization/client/lib/models/Subscriptions.js b/packages/rocketchat-authorization/client/lib/models/Subscriptions.js new file mode 100644 index 0000000000000000000000000000000000000000..7ff02a801037048cd704df777f0a559dce497751 --- /dev/null +++ b/packages/rocketchat-authorization/client/lib/models/Subscriptions.js @@ -0,0 +1,35 @@ +Meteor.subscribe('scopedRoles', 'Subscriptions'); + +if (_.isUndefined(RocketChat.models.Subscriptions)) { + RocketChat.models.Subscriptions = {} +} + +RocketChat.models.Subscriptions.isUserInRole = function(userId, roleName, roomId) { + query = { + rid: roomId, + roles: roleName + }; + + return !_.isUndefined(this.findOne(query)); +} + +RocketChat.models.Subscriptions.findUsersInRoles = function(roles, scope, options) { + roles = [].concat(roles); + + var query = { + roles: { $in: roles } + } + + if (scope) { + query.rid = scope; + } + + subscriptions = this.find(query).fetch(); + + users = _.compact(_.map(subscriptions, function(subscription) { + if ('undefined' !== typeof subscription.u && 'undefined' !== typeof subscription.u._id) + return subscription.u._id + })); + + return RocketChat.models.Users.find({ _id: { $in: users } }, options); +} diff --git a/packages/rocketchat-authorization/client/lib/models/Users.js b/packages/rocketchat-authorization/client/lib/models/Users.js new file mode 100644 index 0000000000000000000000000000000000000000..e7f61495105807867555a801164b9265caf04dcc --- /dev/null +++ b/packages/rocketchat-authorization/client/lib/models/Users.js @@ -0,0 +1,24 @@ +Meteor.subscribe('scopedRoles', 'Users'); + +if (_.isUndefined(RocketChat.models.Users)) { + RocketChat.models.Users = {} +} + +RocketChat.models.Users.isUserInRole = function(userId, roleName) { + query = { + _id: userId, + roles: roleName + }; + + return !_.isUndefined(this.findOne(query)); +} + +RocketChat.models.Users.findUsersInRoles = function(roles, scope, options) { + roles = [].concat(roles); + + var query = { + roles: { $in: roles } + } + + return this.find(query, options); +} diff --git a/packages/rocketchat-authorization/client/startup.coffee b/packages/rocketchat-authorization/client/startup.coffee index 144f7ea88f9ae3f0735cec28551a19b7085bcc5e..61f0527598d0fe448a263d6f5c9d82ca8d390b88 100644 --- a/packages/rocketchat-authorization/client/startup.coffee +++ b/packages/rocketchat-authorization/client/startup.coffee @@ -1,3 +1,5 @@ +Meteor.subscribe 'roles' + RocketChat.authz.subscription = Meteor.subscribe 'permissions' RocketChat.AdminBox.addOption diff --git a/packages/rocketchat-authorization/client/views/permissions.coffee b/packages/rocketchat-authorization/client/views/permissions.coffee index 0e134582401e8e84e1224610ec2572324770ec6c..af92f2c126605ba8bacee8042285ef1fd42c6d01 100644 --- a/packages/rocketchat-authorization/client/views/permissions.coffee +++ b/packages/rocketchat-authorization/client/views/permissions.coffee @@ -8,7 +8,7 @@ Template.permissions.helpers granted: (roles) -> if roles? - return 'checked' if roles.indexOf(@name) isnt -1 + return 'checked' if roles.indexOf(@_id) isnt -1 hasPermission: -> return RocketChat.authz.hasAllPermission 'access-permissions' @@ -31,18 +31,14 @@ Template.permissions.onCreated -> added: {} removed: {} - subs = @subscribe 'roles' - Tracker.autorun => - if subs.ready() - @roles.set Roles.getAllRoles().fetch() + @roles.set RocketChat.models.Roles.find().fetch() Tracker.autorun => - if subs.ready() - ChatPermissions.find().observeChanges - added: (id, fields) => - @permissionByRole[id] = fields.roles - changed: (id, fields) => - @permissionByRole[id] = fields.roles - removed: (id) => - delete @permissionByRole[id] + ChatPermissions.find().observeChanges + added: (id, fields) => + @permissionByRole[id] = fields.roles + changed: (id, fields) => + @permissionByRole[id] = fields.roles + removed: (id) => + delete @permissionByRole[id] diff --git a/packages/rocketchat-authorization/client/views/permissions.html b/packages/rocketchat-authorization/client/views/permissions.html index f95b309f24a35ed785388c21c8971469c6462fa3..c895fea4c2de67b038350d2e55c73b41b3e1c2b8 100644 --- a/packages/rocketchat-authorization/client/views/permissions.html +++ b/packages/rocketchat-authorization/client/views/permissions.html @@ -9,8 +9,8 @@ <td> </td> {{#each role}} <td title="{{description}}"> - <a href="{{pathFor "admin-permissions-edit" name=name}}"> - {{name}} + <a href="{{pathFor "admin-permissions-edit" name=_id}}"> + {{_id}} <i class="icon-edit"></i> </a> </td> @@ -23,7 +23,7 @@ <td>{{_id}}</td> {{#each role}} <td> - <input type="checkbox" name="perm[{{_id}}][{{../_id}}]" class="role-permission" value="1" checked="{{granted ../roles ../_id}}" data-role="{{name}}" data-permission="{{../_id}}"> + <input type="checkbox" name="perm[{{_id}}][{{../_id}}]" class="role-permission" value="1" checked="{{granted ../roles}}" data-role="{{_id}}" data-permission="{{../_id}}"> </td> {{/each}} </tr> diff --git a/packages/rocketchat-authorization/client/views/permissionsRole.coffee b/packages/rocketchat-authorization/client/views/permissionsRole.coffee index bb754ced89c9a40911c5882ca11318aa6b5dbda2..739fa26afada48c8d0a1c6b219db572a6e36e3ba 100644 --- a/packages/rocketchat-authorization/client/views/permissionsRole.coffee +++ b/packages/rocketchat-authorization/client/views/permissionsRole.coffee @@ -1,6 +1,6 @@ Template.permissionsRole.helpers role: -> - return Meteor.roles.findOne({ name: FlowRouter.getParam('name') }) or {} + return RocketChat.models.Roles.findOne({ _id: FlowRouter.getParam('name') }) or {} userInRole: -> return Template.instance().usersInRole @@ -108,4 +108,4 @@ Template.permissionsRole.onCreated -> @subscribe 'roles', FlowRouter.getParam('name') @subscribe 'usersInRole', FlowRouter.getParam('name') - @usersInRole = Roles.getUsersInRole(FlowRouter.getParam('name'), Roles.GLOBAL_GROUP, { sort: { username: 1 } }) + @usersInRole = RocketChat.models.Roles.findUsersInRole(FlowRouter.getParam('name'), null, { sort: { username: 1 } }) diff --git a/packages/rocketchat-authorization/client/views/permissionsRole.html b/packages/rocketchat-authorization/client/views/permissionsRole.html index a824390e886680a18e37f6fc3df8c3021b91c77f..18382fdb3c6a805a096fbfc4c7eec6e780c0b20c 100644 --- a/packages/rocketchat-authorization/client/views/permissionsRole.html +++ b/packages/rocketchat-authorization/client/views/permissionsRole.html @@ -7,7 +7,7 @@ <form id="form-role" class="inline form-role"> <label>{{_ "Role"}} :</label> {{#if editing}} - <span>{{name}}</span> + <span>{{_id}}</span> {{else}} <input type="text" name="name" value=""> {{/if}} diff --git a/packages/rocketchat-authorization/package.js b/packages/rocketchat-authorization/package.js index 62fa6b7fe6460f2a5bcab13eca032473bd73801f..69682e47db333201648a31e4c0ff64e69936b27d 100644 --- a/packages/rocketchat-authorization/package.js +++ b/packages/rocketchat-authorization/package.js @@ -11,18 +11,22 @@ Package.onUse(function(api) { api.use([ 'coffeescript', 'underscore', - 'rocketchat:lib', - 'alanning:roles@1.2.12' + 'rocketchat:lib' ]); - api.use('mongo', 'client'); + api.use('mongo', ['client', 'server']); api.use('kadira:flow-router', 'client'); api.use('less@2.5.1', 'client'); + api.use('tracker', 'client'); api.use('templating', 'client'); api.addFiles('lib/rocketchat.coffee', ['server','client']); - api.addFiles('client/collection.coffee', ['client']); + api.addFiles('client/lib/ChatPermissions.coffee', ['client']); + api.addFiles('client/lib/models/Roles.coffee', ['client']); + api.addFiles('client/lib/models/Users.js', ['client']); + api.addFiles('client/lib/models/Subscriptions.js', ['client']); + api.addFiles('client/startup.coffee', ['client']); api.addFiles('client/hasPermission.coffee', ['client']); api.addFiles('client/hasRole.coffee', ['client']); @@ -39,19 +43,22 @@ Package.onUse(function(api) { api.addFiles('client/stylesheets/permissions.less', 'client'); api.addFiles('server/models/Permissions.coffee', ['server']); + api.addFiles('server/models/Roles.coffee', ['server']); + api.addFiles('server/models/Base.js', ['server']); + api.addFiles('server/models/Users.js', ['server']); + api.addFiles('server/models/Subscriptions.js', ['server']); - api.addFiles('server/functions/addUsersToRoles.coffee', ['server']); - api.addFiles('server/functions/getPermissionsForRole.coffee', ['server']); + api.addFiles('server/functions/addUserRoles.coffee', ['server']); api.addFiles('server/functions/getRoles.coffee', ['server']); - api.addFiles('server/functions/getRolesForUser.coffee', ['server']); api.addFiles('server/functions/getUsersInRole.coffee', ['server']); api.addFiles('server/functions/hasPermission.coffee', ['server']); api.addFiles('server/functions/hasRole.coffee', ['server']); - api.addFiles('server/functions/removeUsersFromRoles.coffee', ['server']); + api.addFiles('server/functions/removeUserFromRoles.coffee', ['server']); // publications - api.addFiles('server/publication.coffee', ['server']); + api.addFiles('server/publications/permissions.js', 'server'); api.addFiles('server/publications/roles.coffee', 'server'); + api.addFiles('server/publications/scopedRoles.js', 'server'); api.addFiles('server/publications/usersInRole.coffee', 'server'); // methods diff --git a/packages/rocketchat-authorization/server/functions/addUserRoles.coffee b/packages/rocketchat-authorization/server/functions/addUserRoles.coffee new file mode 100644 index 0000000000000000000000000000000000000000..c715153ce326174249ddba7290f2051551558997 --- /dev/null +++ b/packages/rocketchat-authorization/server/functions/addUserRoles.coffee @@ -0,0 +1,19 @@ +RocketChat.authz.addUserRoles = (userId, roleNames, scope) -> + if not userId or not roleNames + return false + + user = RocketChat.models.Users.findOneById(userId) + if not user + throw new Meteor.Error 'invalid-user' + + roleNames = [].concat roleNames + + existingRoleNames = _.pluck(RocketChat.authz.getRoles(), '_id') + invalidRoleNames = _.difference(roleNames, existingRoleNames) + unless _.isEmpty(invalidRoleNames) + for role in invalidRoleNames + RocketChat.models.Roles.createOrUpdate role + + RocketChat.models.Roles.addUserRoles(userId, roleNames, scope) + + return true diff --git a/packages/rocketchat-authorization/server/functions/addUsersToRoles.coffee b/packages/rocketchat-authorization/server/functions/addUsersToRoles.coffee deleted file mode 100644 index ac2be57a5460073b5263534400897f2e5f033fee..0000000000000000000000000000000000000000 --- a/packages/rocketchat-authorization/server/functions/addUsersToRoles.coffee +++ /dev/null @@ -1,27 +0,0 @@ -RocketChat.authz.addUsersToRoles = (userIds, roleNames, scope ) -> - if not userIds or not roleNames - return false - - unless _.isArray(userIds) - userIds = [userIds] - - users = Meteor.users.find({_id: {$in : userIds}}).fetch() - unless userIds.length is users.length - throw new Meteor.Error 'invalid-user' - - unless _.isArray(roleNames) - roleNames = [roleNames] - - existingRoleNames = _.pluck(RocketChat.authz.getRoles().fetch(), 'name') - invalidRoleNames = _.difference( roleNames, existingRoleNames) - unless _.isEmpty(invalidRoleNames) - # throw new Meteor.Error 'invalid-role' - for role in invalidRoleNames - Roles.createRole role - - unless _.isString(scope) - scope = Roles.GLOBAL_GROUP - - Roles.addUsersToRoles( userIds, roleNames, scope) - - return true diff --git a/packages/rocketchat-authorization/server/functions/getPermissionsForRole.coffee b/packages/rocketchat-authorization/server/functions/getPermissionsForRole.coffee deleted file mode 100644 index 7023d120265bdf8e1e41d5062538f4ae116d90ea..0000000000000000000000000000000000000000 --- a/packages/rocketchat-authorization/server/functions/getPermissionsForRole.coffee +++ /dev/null @@ -1,9 +0,0 @@ -RocketChat.authz.getPermissionsForRole = (roleName) -> - unless roleName - throw new Meteor.Error 'invalid-role' - - roleNames = _.pluck(RocketChat.authz.getRoles().fetch(), 'name') - unless roleName in roleNames - throw new Meteor.Error 'invalid-role', "Role #{roleName} not found" - - return _.pluck(RocketChat.models.Permissions.findByRole( roleName ).fetch(), '_id') diff --git a/packages/rocketchat-authorization/server/functions/getRoles.coffee b/packages/rocketchat-authorization/server/functions/getRoles.coffee index 37c3d2537fdb438dcc9afc654e5eb1d941ef69af..895dcc532c1913c7fdb53e8800c8d079ecc4f8e8 100644 --- a/packages/rocketchat-authorization/server/functions/getRoles.coffee +++ b/packages/rocketchat-authorization/server/functions/getRoles.coffee @@ -1,2 +1,2 @@ RocketChat.authz.getRoles = -> - return Roles.getAllRoles() \ No newline at end of file + return RocketChat.models.Roles.find().fetch() diff --git a/packages/rocketchat-authorization/server/functions/getRolesForUser.coffee b/packages/rocketchat-authorization/server/functions/getRolesForUser.coffee deleted file mode 100644 index 800473a08bf3d14650a1fee5717714f62448e977..0000000000000000000000000000000000000000 --- a/packages/rocketchat-authorization/server/functions/getRolesForUser.coffee +++ /dev/null @@ -1,6 +0,0 @@ -RocketChat.authz.getRolesForUser = (userId, scope) -> - # returns roles for the given scope as well as the global scope - unless scope - scope = Roles.GLOBAL_GROUP - - return Roles.getRolesForUser(userId, scope) diff --git a/packages/rocketchat-authorization/server/functions/getUsersInRole.coffee b/packages/rocketchat-authorization/server/functions/getUsersInRole.coffee index ee416c3e90255a1dddb77cb42ec78b055676126e..b8fc11a51fbae3c0462bab59d41e59392782e547 100644 --- a/packages/rocketchat-authorization/server/functions/getUsersInRole.coffee +++ b/packages/rocketchat-authorization/server/functions/getUsersInRole.coffee @@ -1,6 +1,2 @@ RocketChat.authz.getUsersInRole = (roleName, scope, options) -> - # alanning:roles doc says this is an expensive operation - unless _.isString(scope) - scope = Roles.GLOBAL_GROUP - - return Roles.getUsersInRole(roleName, scope, options) + return RocketChat.models.Roles.findUsersInRole(roleName, scope, options) diff --git a/packages/rocketchat-authorization/server/functions/hasPermission.coffee b/packages/rocketchat-authorization/server/functions/hasPermission.coffee index b7c39299c7edaba68f68539e2b054cc7099c7a1d..7ae014453d0e994254bfa534be5054163f970e60 100644 --- a/packages/rocketchat-authorization/server/functions/hasPermission.coffee +++ b/packages/rocketchat-authorization/server/functions/hasPermission.coffee @@ -1,10 +1,3 @@ RocketChat.authz.hasPermission = (userId, permissionId, scope) -> - # get user's roles - roles = RocketChat.authz.getRolesForUser(userId, scope) - - # get permissions for user's roles - permissions = [] - for role in roles - permissions = permissions.concat( RocketChat.authz.getPermissionsForRole( role )) - # may contain duplicate, but doesn't matter - return permissionId in permissions + permission = RocketChat.models.Permissions.findOne permissionId + return RocketChat.models.Roles.isUserInRoles(userId, permission.roles, scope) diff --git a/packages/rocketchat-authorization/server/functions/hasRole.coffee b/packages/rocketchat-authorization/server/functions/hasRole.coffee index 10ac10df4428c504fbc4f61acd15e33c0a53dca7..aff335e48b2fd0f7ae37fce3f2d00bb98796124c 100644 --- a/packages/rocketchat-authorization/server/functions/hasRole.coffee +++ b/packages/rocketchat-authorization/server/functions/hasRole.coffee @@ -1,3 +1,3 @@ -RocketChat.authz.hasRole = (userId, roleName, scope) -> - # per alanning:roles, returns true if user is in ANY roles - return Roles.userIsInRole(userId, [roleName], scope) +RocketChat.authz.hasRole = (userId, roleNames, scope) -> + roleNames = [].concat roleNames + return RocketChat.models.Roles.isUserInRoles(userId, roleNames, scope) # true if user is in ANY role diff --git a/packages/rocketchat-authorization/server/functions/removeUserFromRoles.coffee b/packages/rocketchat-authorization/server/functions/removeUserFromRoles.coffee new file mode 100644 index 0000000000000000000000000000000000000000..64df765f67fc7711960861e2f4b8543287b8afce --- /dev/null +++ b/packages/rocketchat-authorization/server/functions/removeUserFromRoles.coffee @@ -0,0 +1,18 @@ +RocketChat.authz.removeUserFromRoles = (userId, roleNames, scope) -> + if not userId or not roleNames + return false + + user = RocketChat.models.Users.findOneById(userId) + if not user? + throw new Meteor.Error 'invalid-user' + + roleNames = [].concat roleNames + + existingRoleNames = _.pluck(RocketChat.authz.getRoles(), 'name') + invalidRoleNames = _.difference(roleNames, existingRoleNames) + unless _.isEmpty(invalidRoleNames) + throw new Meteor.Error 'invalid-role' + + RocketChat.models.Roles.removeUserRoles(userId, roleNames, scope) + + return true diff --git a/packages/rocketchat-authorization/server/functions/removeUsersFromRoles.coffee b/packages/rocketchat-authorization/server/functions/removeUsersFromRoles.coffee deleted file mode 100644 index 93f78b6ccad5b1af25aaf44a20de37e490bdc89f..0000000000000000000000000000000000000000 --- a/packages/rocketchat-authorization/server/functions/removeUsersFromRoles.coffee +++ /dev/null @@ -1,25 +0,0 @@ -RocketChat.authz.removeUsersFromRoles = (userIds, roleNames, scope ) -> - if not userIds or not roleNames - return false - - unless _.isArray(userIds) - userIds = [userIds] - - users = Meteor.users.find({_id: {$in : userIds}}).fetch() - unless userIds.length is users.length - throw new Meteor.Error 'invalid-user' - - unless _.isArray(roleNames) - roleNames = [roleNames] - - existingRoleNames = _.pluck(RocketChat.authz.getRoles().fetch(), 'name') - invalidRoleNames = _.difference( roleNames, existingRoleNames) - unless _.isEmpty(invalidRoleNames) - throw new Meteor.Error 'invalid-role' - - unless _.isString(scope) - scope = Roles.GLOBAL_GROUP - - Roles.removeUsersFromRoles( userIds, roleNames, scope) - - return true diff --git a/packages/rocketchat-authorization/server/methods/addUserToRole.coffee b/packages/rocketchat-authorization/server/methods/addUserToRole.coffee index a5340768b512a68b516f303f4a1fed499bc284a2..74ba6bbea53017285a33b185a525663ef2bd96ba 100644 --- a/packages/rocketchat-authorization/server/methods/addUserToRole.coffee +++ b/packages/rocketchat-authorization/server/methods/addUserToRole.coffee @@ -1,15 +1,14 @@ Meteor.methods - 'authorization:addUserToRole': (roleName, username) -> + 'authorization:addUserToRole': (roleName, username, scope) -> if not Meteor.userId() or not RocketChat.authz.hasPermission Meteor.userId(), 'access-permissions' throw new Meteor.Error "not-authorized" if not roleName or not _.isString(roleName) or not username or not _.isString(username) throw new Meteor.Error 'invalid-arguments' - user = Meteor.users.findOne { username: username }, { fields: { _id: 1 } } + user = RocketChat.models.Users.findOneByUsername username, { fields: { _id: 1 } } if not user?._id? throw new Meteor.Error 'user-not-found', 'User_not_found' - # return Roles.addUsersToRoles user._id, roleName - return Roles.addUsersToRoles user._id, roleName, Roles.GLOBAL_GROUP + return RocketChat.models.Roles.addUserRoles user._id, roleName, scope diff --git a/packages/rocketchat-authorization/server/methods/deleteRole.coffee b/packages/rocketchat-authorization/server/methods/deleteRole.coffee index 0a928cb252bd089fa2077e6c7601e8f0f50800f8..39ca53a350289761cca1c9725c2ebe8d70cf6dd4 100644 --- a/packages/rocketchat-authorization/server/methods/deleteRole.coffee +++ b/packages/rocketchat-authorization/server/methods/deleteRole.coffee @@ -1,16 +1,19 @@ Meteor.methods - 'authorization:deleteRole': (_id) -> + 'authorization:deleteRole': (roleName) -> if not Meteor.userId() or not RocketChat.authz.hasPermission Meteor.userId(), 'access-permissions' throw new Meteor.Error "not-authorized" - role = Meteor.roles.findOne _id + role = RocketChat.models.Roles.findOne roleName + if not role? + throw new Meteor.Error 'invalid-role' if role.protected throw new Meteor.Error 'protected-role', 'Cannot_delete_a_protected_role' - someone = Meteor.users.findOne { "roles.#{Roles.GLOBAL_GROUP}": role.name } + roleScope = role.scope or 'Users' + existingUsers = RocketChat.models[roleScope]?.findUsersInRoles?(roleName) - if someone? + if existingUsers?.count() > 0 throw new Meteor.Error 'role-in-use', 'Cannot_delete_role_because_its_in_use' - return Roles.deleteRole role.name + return RocketChat.models.Roles.remove role.name diff --git a/packages/rocketchat-authorization/server/methods/removeUserFromRole.coffee b/packages/rocketchat-authorization/server/methods/removeUserFromRole.coffee index 6510c2f8b2be8683da88afda7d36a074762adc54..26da340af6f32bfd70bab9179e3aebff25a58734 100644 --- a/packages/rocketchat-authorization/server/methods/removeUserFromRole.coffee +++ b/packages/rocketchat-authorization/server/methods/removeUserFromRole.coffee @@ -11,4 +11,4 @@ Meteor.methods if not user?._id? throw new Meteor.Error 'user-not-found' - return Roles.removeUsersFromRoles user._id, roleName, Roles.GLOBAL_GROUP + return RocketChat.models.Roles.removeUserRoles user._id, roleName diff --git a/packages/rocketchat-authorization/server/methods/saveRole.coffee b/packages/rocketchat-authorization/server/methods/saveRole.coffee index 7a30d9ecec60571c4bbaea899dbfc40c6043eaeb..874fe0eaa6d0bc3de8ba8c3d1e178e857b42069a 100644 --- a/packages/rocketchat-authorization/server/methods/saveRole.coffee +++ b/packages/rocketchat-authorization/server/methods/saveRole.coffee @@ -3,13 +3,7 @@ Meteor.methods if not Meteor.userId() or not RocketChat.authz.hasPermission Meteor.userId(), 'access-permissions' throw new Meteor.Error "not-authorized" - saveData = - description: roleData.description + if not roleData.name? + throw new Meteor.Error 'invalid-data', 'Role name is required' - if not _id? and roleData.name? - saveData.name = roleData.name - - if _id? - return Meteor.roles.update _id, { $set: saveData } - else - return Meteor.roles.insert saveData + return RocketChat.models.Roles.createOrUpdate roleData.name, 'Users', roleData.description diff --git a/packages/rocketchat-authorization/server/models/Base.js b/packages/rocketchat-authorization/server/models/Base.js new file mode 100644 index 0000000000000000000000000000000000000000..5977c6446677641d03f7e66ada585774895224d0 --- /dev/null +++ b/packages/rocketchat-authorization/server/models/Base.js @@ -0,0 +1,38 @@ +RocketChat.models._Base.prototype.roleBaseQuery = function(userId, scope) { return {} } + +RocketChat.models._Base.prototype.findRolesByUserId = function(userId, options) { + var query = this.roleBaseQuery(userId); + return this.find(query, { fields: { roles: 1 } }); +} + +RocketChat.models._Base.prototype.isUserInRole = function(userId, roleName, scope) { + var query = this.roleBaseQuery(userId, scope); + query.roles = roleName; + return !_.isUndefined(this.findOne(query)); +} + +RocketChat.models._Base.prototype.addRolesByUserId = function(userId, roles, scope) { + var roles = [].concat(roles); + var query = this.roleBaseQuery(userId, scope); + var update = { + $addToSet: { + roles: { $each: roles } + } + } + return this.update(query, update); +} + +RocketChat.models._Base.prototype.removeRolesByUserId = function(userId, roles, scope) { + var roles = [].concat(roles); + var query = this.roleBaseQuery(userId, scope); + var update = { + $pullAll: { + roles: roles + } + } + return this.update(query, update); +} + +RocketChat.models._Base.prototype.findUsersInRoles = function() { + throw new Meteor.Error('overwrite-function', 'You must overwrite this function in the extended classes'); +} diff --git a/packages/rocketchat-authorization/server/models/Permissions.coffee b/packages/rocketchat-authorization/server/models/Permissions.coffee index 3e1c6eef96a5e025b65cbbad6fe3929ea2e7a8fc..ec763d94b7122a1d14dcde4db00e4f26b850b792 100644 --- a/packages/rocketchat-authorization/server/models/Permissions.coffee +++ b/packages/rocketchat-authorization/server/models/Permissions.coffee @@ -2,7 +2,6 @@ RocketChat.models.Permissions = new class extends RocketChat.models._Base constructor: -> @_initModel 'permissions' - # FIND findByRole: (role, options) -> query = diff --git a/packages/rocketchat-authorization/server/models/Roles.coffee b/packages/rocketchat-authorization/server/models/Roles.coffee new file mode 100644 index 0000000000000000000000000000000000000000..8489afa1d47ac77c94b8acc537d88bab2faa09d8 --- /dev/null +++ b/packages/rocketchat-authorization/server/models/Roles.coffee @@ -0,0 +1,43 @@ +RocketChat.models.Roles = new class extends RocketChat.models._Base + constructor: -> + @_initModel 'roles' + @tryEnsureIndex { 'name': 1 } + @tryEnsureIndex { 'scope': 1 } + + findUsersInRole: (name, scope, options) -> + role = @findOne name + roleScope = role?.scope or 'Users' + RocketChat.models[roleScope]?.findUsersInRoles?(name, scope, options) + + isUserInRoles: (userId, roles, scope) -> + roles = [].concat roles + _.some roles, (roleName) => + role = @findOne roleName + roleScope = role?.scope or 'Users' + return RocketChat.models[roleScope]?.isUserInRole?(userId, roleName, scope) + + createOrUpdate: (name, scope, description, protectedRole) -> + scope ?= 'Users' + updateData = {} + updateData.name = name + updateData.scope = scope + if description? + updateData.description = description + if protectedRole? + updateData.protected = protectedRole + + @upsert { _id: name }, { $set: updateData } + + addUserRoles: (userId, roles, scope) -> + roles = [].concat roles + for roleName in roles + role = @findOne roleName + roleScope = role?.scope or 'Users' + RocketChat.models[roleScope]?.addRolesByUserId?(userId, roleName, scope) + + removeUserRoles: (userId, roles, scope) -> + roles = [].concat roles + for roleName in roles + role = @findOne roleName + roleScope = role?.scope or 'Users' + RocketChat.models[roleScope]?.removeRolesByUserId?(userId, roleName, scope) diff --git a/packages/rocketchat-authorization/server/models/Subscriptions.js b/packages/rocketchat-authorization/server/models/Subscriptions.js new file mode 100644 index 0000000000000000000000000000000000000000..9c7d4a16fc760f753d6109bc14d64932d245aa05 --- /dev/null +++ b/packages/rocketchat-authorization/server/models/Subscriptions.js @@ -0,0 +1,28 @@ +RocketChat.models.Subscriptions.roleBaseQuery = function(userId, scope) { + var query = { "u._id": userId } + if (!_.isUndefined(scope)) { + query.rid = scope; + } + return query; +} + +RocketChat.models.Subscriptions.findUsersInRoles = function(roles, scope, options) { + roles = [].concat(roles); + + var query = { + roles: { $in: roles } + } + + if (scope) { + query.rid = scope; + } + + subscriptions = this.find(query).fetch(); + + users = _.compact(_.map(subscriptions, function(subscription) { + if ('undefined' !== typeof subscription.u && 'undefined' !== typeof subscription.u._id) + return subscription.u._id + })); + + return RocketChat.models.Users.find({ _id: { $in: users } }, options); +} diff --git a/packages/rocketchat-authorization/server/models/Users.js b/packages/rocketchat-authorization/server/models/Users.js new file mode 100644 index 0000000000000000000000000000000000000000..315ab7e9acf9500536ff5c9ca491bc380d1f3970 --- /dev/null +++ b/packages/rocketchat-authorization/server/models/Users.js @@ -0,0 +1,13 @@ +RocketChat.models.Users.roleBaseQuery = function(userId, scope) { + return { _id: userId } +} + +RocketChat.models.Users.findUsersInRoles = function(roles, scope, options) { + roles = [].concat(roles); + + var query = { + roles: { $in: roles } + } + + return this.find(query, options); +} diff --git a/packages/rocketchat-authorization/server/publication.coffee b/packages/rocketchat-authorization/server/publication.coffee deleted file mode 100644 index 9a4fa8cbfc1e737fc08741fa78fd10c1d3966afd..0000000000000000000000000000000000000000 --- a/packages/rocketchat-authorization/server/publication.coffee +++ /dev/null @@ -1,2 +0,0 @@ -Meteor.publish 'permissions', -> - return RocketChat.models.Permissions.find {} diff --git a/packages/rocketchat-authorization/server/publications/integrations.coffee b/packages/rocketchat-authorization/server/publications/integrations.coffee deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/packages/rocketchat-authorization/server/publications/permissions.js b/packages/rocketchat-authorization/server/publications/permissions.js new file mode 100644 index 0000000000000000000000000000000000000000..0872d3b4db046cc84791c55c1e576b7399bd4218 --- /dev/null +++ b/packages/rocketchat-authorization/server/publications/permissions.js @@ -0,0 +1,3 @@ +Meteor.publish('permissions', function () { + return RocketChat.models.Permissions.find({}); +}); diff --git a/packages/rocketchat-authorization/server/publications/roles.coffee b/packages/rocketchat-authorization/server/publications/roles.coffee index 6357a62510e7c6a4635d19a169531248d04a0145..b3eafb56d3e50b2a485ab95413f4a4064755d776 100644 --- a/packages/rocketchat-authorization/server/publications/roles.coffee +++ b/packages/rocketchat-authorization/server/publications/roles.coffee @@ -2,7 +2,5 @@ Meteor.publish 'roles', -> unless @userId return @ready() - if not RocketChat.authz.hasPermission @userId, 'access-permissions' - throw new Meteor.Error "not-authorized" + return RocketChat.models.Roles.find() - return RocketChat.authz.getRoles() diff --git a/packages/rocketchat-authorization/server/publications/scopedRoles.js b/packages/rocketchat-authorization/server/publications/scopedRoles.js new file mode 100644 index 0000000000000000000000000000000000000000..bd6247697b0abb90c100593ea93c9aa097bdaf58 --- /dev/null +++ b/packages/rocketchat-authorization/server/publications/scopedRoles.js @@ -0,0 +1,11 @@ +/** + * Publish logged-in user's roles so client-side checks can work. + */ +Meteor.publish('scopedRoles', function (scope) { + if (!this.userId || _.isUndefined(RocketChat.models[scope]) || !_.isFunction(RocketChat.models[scope].findRolesByUserId)) { + this.ready() + return + } + + return RocketChat.models[scope].findRolesByUserId(this.userId); +}); diff --git a/packages/rocketchat-authorization/server/startup.coffee b/packages/rocketchat-authorization/server/startup.coffee index fbaf7bdb701c522c20c6301b9cc8b35d5e05b208..d9bd6b1a7b12cce3b3bf0958a85d7c81fce87b88 100644 --- a/packages/rocketchat-authorization/server/startup.coffee +++ b/packages/rocketchat-authorization/server/startup.coffee @@ -7,7 +7,7 @@ Meteor.startup -> permissions = [ { _id: 'view-statistics', - roles : ['admin', 'temp-role']} + roles : ['admin']} { _id: 'view-privileged-setting', roles : ['admin']} @@ -34,7 +34,7 @@ Meteor.startup -> roles : ['admin']} { _id: 'edit-other-user-active-status', - roles : ['admin', 'site-moderator']} + roles : ['admin']} { _id: 'delete-user', roles : ['admin']} @@ -49,37 +49,37 @@ Meteor.startup -> roles : ['admin']} { _id: 'create-c', - roles : ['admin', 'site-moderator', 'user']} + roles : ['admin', 'user']} { _id: 'delete-c', - roles : ['admin', 'site-moderator']} + roles : ['admin']} { _id: 'edit-room', - roles : ['admin', 'site-moderator', 'moderator']} + roles : ['admin', 'moderator']} { _id: 'edit-message', - roles : ['admin', 'site-moderator', 'moderator']} + roles : ['admin', 'moderator']} { _id: 'delete-message', - roles : ['admin', 'site-moderator', 'moderator']} + roles : ['admin', 'moderator']} { _id: 'remove-user', - roles : ['admin', 'site-moderator', 'moderator']} + roles : ['admin', 'moderator']} { _id: 'mute-user', - roles : ['admin', 'site-moderator', 'moderator']} + roles : ['admin', 'moderator']} { _id: 'ban-user', - roles : ['admin', 'site-moderator', 'moderator']} + roles : ['admin', 'moderator']} { _id: 'create-p', - roles : ['admin', 'site-moderator', 'user']} + roles : ['admin', 'user']} { _id: 'delete-p', - roles : ['admin', 'site-moderator']} + roles : ['admin']} { _id: 'delete-d', - roles : ['admin', 'site-moderator']} + roles : ['admin']} { _id: 'bulk-register-user', roles : ['admin']} @@ -88,13 +88,13 @@ Meteor.startup -> roles : ['admin']} { _id: 'view-c-room', - roles : ['admin', 'site-moderator', 'user']} + roles : ['admin', 'user']} { _id: 'view-p-room', - roles : ['admin', 'site-moderator', 'user']} + roles : ['admin', 'user']} { _id: 'view-d-room', - roles : ['admin', 'site-moderator', 'user']} + roles : ['admin', 'user']} { _id: 'access-permissions', roles : ['admin']} @@ -109,14 +109,15 @@ Meteor.startup -> roles : ['admin']} ] - #alanning:roles - roles = _.pluck(Roles.getAllRoles().fetch(), 'name'); - for permission in permissions RocketChat.models.Permissions.upsert( permission._id, {$set: permission }) - for role in permission.roles - unless role in roles - Roles.createRole role - roles.push(role) + defaultRoles = [ + { name: 'admin', scope: 'Users' } + { name: 'moderator', scope: 'Subscriptions' } + { name: 'user', scope: 'Users' } + { name: 'bot', scope: 'Users' } + ] + for role in defaultRoles + RocketChat.models.Roles.createOrUpdate role.name, role.scope diff --git a/packages/rocketchat-integrations/package.js b/packages/rocketchat-integrations/package.js index 8c00200f332156a62c6db0b648cc78b05c4d5992..dc2b48f48e4a2a2c22c6ceb78be6965e019e5743 100644 --- a/packages/rocketchat-integrations/package.js +++ b/packages/rocketchat-integrations/package.js @@ -13,7 +13,7 @@ Package.onUse(function(api) { api.use('underscore'); api.use('simple:highlight.js'); api.use('rocketchat:lib'); - api.use('alanning:roles@1.2.12'); + api.use('rocketchat:authorization'); api.use('kadira:flow-router', 'client'); api.use('templating', 'client'); diff --git a/packages/rocketchat-integrations/server/methods/incoming/addIncomingIntegration.coffee b/packages/rocketchat-integrations/server/methods/incoming/addIncomingIntegration.coffee index 67c33d156f6a745f1e393e3cb8c93c23cf1d8191..54efaf760064dd44e6fc5c07c564f2fc0bca3733 100644 --- a/packages/rocketchat-integrations/server/methods/incoming/addIncomingIntegration.coffee +++ b/packages/rocketchat-integrations/server/methods/incoming/addIncomingIntegration.coffee @@ -61,7 +61,7 @@ Meteor.methods RocketChat.models.Users.update {_id: user._id}, updateObj - Roles.addUsersToRoles user._id, 'bot', 'bot' + RocketChat.models.Roles.addUserRoles user._id, 'bot' integration._id = RocketChat.models.Integrations.insert integration diff --git a/packages/rocketchat-integrations/server/methods/incoming/updateIncomingIntegration.coffee b/packages/rocketchat-integrations/server/methods/incoming/updateIncomingIntegration.coffee index da7aa3dd15f2f80ae1b67477bfc8e936a0b6249b..59a93bdbbafae5e788a0cee499d1aa0fd23ece1e 100644 --- a/packages/rocketchat-integrations/server/methods/incoming/updateIncomingIntegration.coffee +++ b/packages/rocketchat-integrations/server/methods/incoming/updateIncomingIntegration.coffee @@ -38,7 +38,7 @@ Meteor.methods throw new Meteor.Error 'channel_does_not_exists', "[methods] updateIncomingIntegration -> The channel does not exists" user = RocketChat.models.Users.findOne({username: currentIntegration.username}) - Roles.addUsersToRoles user._id, 'bot', 'bot' + RocketChat.models.Roles.addUserRoles user._id, 'bot' RocketChat.models.Integrations.update integrationId, $set: diff --git a/packages/rocketchat-lib/server/methods/setAdminStatus.coffee b/packages/rocketchat-lib/server/methods/setAdminStatus.coffee index b81d6d9f04f62db1ee3817794d0afd9854d3f5c2..542c5853fd094f682c17af4225fdc2527cf22cb4 100644 --- a/packages/rocketchat-lib/server/methods/setAdminStatus.coffee +++ b/packages/rocketchat-lib/server/methods/setAdminStatus.coffee @@ -7,8 +7,8 @@ Meteor.methods throw new Meteor.Error 'not-authorized', '[methods] setAdminStatus -> Not authorized' if admin - RocketChat.authz.addUsersToRoles( userId, 'admin') + RocketChat.authz.addUserRoles( userId, 'admin') else - RocketChat.authz.removeUsersFromRoles( userId, 'admin') + RocketChat.authz.removeUserFromRoles( userId, 'admin') return true diff --git a/packages/rocketchat-livechat/package.js b/packages/rocketchat-livechat/package.js index 216d35f024349e18870575da04daf31727c017a3..a43697f53401f55a0dc46d584a5511487b86c5b8 100644 --- a/packages/rocketchat-livechat/package.js +++ b/packages/rocketchat-livechat/package.js @@ -20,8 +20,8 @@ Package.onUse(function(api) { api.use(['webapp', 'autoupdate'], 'server'); api.use('ecmascript'); - api.use('alanning:roles@1.2.12'); api.use('rocketchat:lib'); + api.use('rocketchat:authorization'); api.use('rocketchat:ui'); api.use('kadira:flow-router', 'client'); api.use('templating', 'client'); diff --git a/packages/rocketchat-livechat/permissions.js b/packages/rocketchat-livechat/permissions.js index ea678f9abe66d7b358754b1dfa7efddfbd436670..ad5a5a3c8432579935162307bdfcc041e42a4ae3 100644 --- a/packages/rocketchat-livechat/permissions.js +++ b/packages/rocketchat-livechat/permissions.js @@ -1,13 +1,13 @@ Meteor.startup(() => { - var roles = _.pluck(Roles.getAllRoles().fetch(), 'name'); + var roles = _.pluck(RocketChat.models.Roles.find().fetch(), 'name'); if (roles.indexOf('livechat-agent') === -1) { - Roles.createRole('livechat-agent'); + RocketChat.models.Roles.createOrUpdate('livechat-agent'); } if (roles.indexOf('livechat-manager') === -1) { - Roles.createRole('livechat-manager'); + RocketChat.models.Roles.createOrUpdate('livechat-manager'); } if (roles.indexOf('livechat-guest') === -1) { - Roles.createRole('livechat-guest'); + RocketChat.models.Roles.createOrUpdate('livechat-guest'); } if (RocketChat.models && RocketChat.models.Permissions) { RocketChat.models.Permissions.createOrUpdate('view-l-room', ['livechat-agent', 'livechat-manager', 'admin']); diff --git a/packages/rocketchat-livechat/server/methods/addAgent.js b/packages/rocketchat-livechat/server/methods/addAgent.js index cd924c14ce9b7d18d5136b8ee78afd49f0cb2a81..e236bc2c41a2f70ef0436a0239434ce0822d8b48 100644 --- a/packages/rocketchat-livechat/server/methods/addAgent.js +++ b/packages/rocketchat-livechat/server/methods/addAgent.js @@ -14,7 +14,7 @@ Meteor.methods({ throw new Meteor.Error('user-not-found', 'Username_not_found'); } - if (RocketChat.authz.addUsersToRoles(user._id, 'livechat-agent')) { + if (RocketChat.authz.addUserRoles(user._id, 'livechat-agent')) { return RocketChat.models.Users.setOperator(user._id, true); } diff --git a/packages/rocketchat-livechat/server/methods/addManager.js b/packages/rocketchat-livechat/server/methods/addManager.js index eb0f905ec402ba01ee5b4b27ee4e9eed4339ab21..9f4af3713a4647d0d0d2feb43197c93d89f6e753 100644 --- a/packages/rocketchat-livechat/server/methods/addManager.js +++ b/packages/rocketchat-livechat/server/methods/addManager.js @@ -14,6 +14,6 @@ Meteor.methods({ throw new Meteor.Error('user-not-found', 'Username_not_found'); } - return RocketChat.authz.addUsersToRoles(user._id, 'livechat-manager'); + return RocketChat.authz.addUserRoles(user._id, 'livechat-manager'); } }); diff --git a/packages/rocketchat-livechat/server/methods/removeAgent.js b/packages/rocketchat-livechat/server/methods/removeAgent.js index 141b7d3bf94350bb164e94814f78d5576bbe25cb..adbf4ae47739efcb49602609dade49d4e62c6fc0 100644 --- a/packages/rocketchat-livechat/server/methods/removeAgent.js +++ b/packages/rocketchat-livechat/server/methods/removeAgent.js @@ -14,7 +14,7 @@ Meteor.methods({ throw new Meteor.Error('user-not-found', 'Username_not_found'); } - if (RocketChat.authz.removeUsersFromRoles(user._id, 'livechat-agent')) { + if (RocketChat.authz.removeUserFromRoles(user._id, 'livechat-agent')) { return RocketChat.models.Users.setOperator(user._id, false); } diff --git a/packages/rocketchat-livechat/server/methods/removeManager.js b/packages/rocketchat-livechat/server/methods/removeManager.js index 32db557fd805773d01c0fb4430c8655d845cd8bb..31ad6cd337b8f01524536b12474e7ce0440cec7f 100644 --- a/packages/rocketchat-livechat/server/methods/removeManager.js +++ b/packages/rocketchat-livechat/server/methods/removeManager.js @@ -12,6 +12,6 @@ Meteor.methods({ throw new Meteor.Error('user-not-found', 'Username_not_found'); } - return RocketChat.authz.removeUsersFromRoles(user._id, 'livechat-manager'); + return RocketChat.authz.removeUserFromRoles(user._id, 'livechat-manager'); } }); diff --git a/packages/rocketchat-livechat/server/models/Users.js b/packages/rocketchat-livechat/server/models/Users.js index 41e10fad9dbc193264869a06f3bbde3562d223ef..7a9d212867d8f84a6cd1861d63d5770642eb4f80 100644 --- a/packages/rocketchat-livechat/server/models/Users.js +++ b/packages/rocketchat-livechat/server/models/Users.js @@ -20,11 +20,9 @@ RocketChat.models.Users.setOperator = function(_id, operator) { RocketChat.models.Users.findOnlineAgents = function() { var query = { status: 'online', - roles: {} + roles: 'livechat-agent' }; - query.roles[Roles.GLOBAL_GROUP] = 'livechat-agent'; - return this.find(query); }; @@ -50,11 +48,10 @@ RocketChat.models.Users.findOnlineUserFromList = function(userList) { */ RocketChat.models.Users.getNextAgent = function() { var query = { - status: 'online' + status: 'online', + roles: 'livechat-agent' }; - query['roles.' + Roles.GLOBAL_GROUP] = 'livechat-agent'; - var collectionObj = this.model.rawCollection(); var findAndModify = Meteor.wrapAsync(collectionObj.findAndModify, collectionObj); diff --git a/packages/rocketchat-oauth2-server-config/admin/client/views/oauthApp.coffee b/packages/rocketchat-oauth2-server-config/admin/client/views/oauthApp.coffee index d8d3b415c34eaca83b8f75c771317d1afcd54bc9..4ced37677bb6857a9cc90107dde8be905b78d1fc 100644 --- a/packages/rocketchat-oauth2-server-config/admin/client/views/oauthApp.coffee +++ b/packages/rocketchat-oauth2-server-config/admin/client/views/oauthApp.coffee @@ -1,5 +1,6 @@ Template.oauthApp.onCreated -> - @record = new ReactiveVar {} + @record = new ReactiveVar + active: true Template.oauthApp.helpers @@ -12,6 +13,9 @@ Template.oauthApp.helpers if params?.id? data = ChatOAuthApps.findOne({_id: params.id}) if data? + data.authorization_url = Meteor.absoluteUrl("oauth/authorize") + data.access_token_url = Meteor.absoluteUrl("oauth/token") + Template.instance().record.set data return data @@ -45,6 +49,7 @@ Template.oauthApp.events "click .submit > .save": -> name = $('[name=name]').val().trim() + active = $('[name=active]:checked').val().trim() is "1" redirectUri = $('[name=redirectUri]').val().trim() if name is '' @@ -55,6 +60,7 @@ Template.oauthApp.events app = name: name + active: active redirectUri: redirectUri params = Template.instance().data.params?() diff --git a/packages/rocketchat-oauth2-server-config/admin/client/views/oauthApp.html b/packages/rocketchat-oauth2-server-config/admin/client/views/oauthApp.html index fab7358c2178d1feb3035f3af84d19540e7e6ab2..d9d722ddd4917d53feaf9ca48f42330c82c2174b 100644 --- a/packages/rocketchat-oauth2-server-config/admin/client/views/oauthApp.html +++ b/packages/rocketchat-oauth2-server-config/admin/client/views/oauthApp.html @@ -5,10 +5,17 @@ <div class="rocket-form"> <div class="section"> <div class="section-content"> + <div class="input-line double-col"> + <label>{{_ "Active"}}</label> + <div> + <label><input class="input-monitor" type="radio" name="active" value="1" checked="{{$eq data.active true}}" /> {{_ "True"}}</label> + <label><input class="input-monitor" type="radio" name="active" value="0" checked="{{$eq data.active false}}" /> {{_ "False"}}</label> + </div> + </div> <div class="input-line double-col"> <label>{{_ "Application_Name"}}</label> <div> - <input type="text" name="name" value="{{data.name}}" placeholder="{{_ 'Optional'}}" /> + <input type="text" name="name" value="{{data.name}}" /> <div class="settings-description">{{_ "Give_the_application_a_name_This_will_be_seen_by_your_users"}}</div> </div> </div> @@ -34,11 +41,25 @@ <div class="settings-description"><a href="#" class="clipboard" data-clipboard-target="[name=clientSecret]">{{_ "COPY_TO_CLIPBOARD"}}</a></div> </div> </div> + <div class="input-line double-col"> + <label>{{_ "Authorization_URL"}}</label> + <div> + <input type="text" name="authorization_url" value="{{data.authorization_url}}" disabled="disabled" /> + <div class="settings-description"><a href="#" class="clipboard" data-clipboard-target="[name=authorization_url]">{{_ "COPY_TO_CLIPBOARD"}}</a></div> + </div> + </div> + <div class="input-line double-col"> + <label>{{_ "Access_Token_URL"}}</label> + <div> + <input type="text" name="access_token_url" value="{{data.access_token_url}}" disabled="disabled" /> + <div class="settings-description"><a href="#" class="clipboard" data-clipboard-target="[name=access_token_url]">{{_ "COPY_TO_CLIPBOARD"}}</a></div> + </div> + </div> {{/if}} </div> </div> <div class="submit"> - {{#if data.token}} + {{#if data.clientId}} <button class="button red delete"><i class="icon-trash"></i><span>{{_ "Delete"}}</span></button> {{/if}} <button class="button save"><i class="icon-send"></i><span>{{_ "Save_changes"}}</span></button> diff --git a/packages/rocketchat-oauth2-server-config/admin/server/methods/addOAuthApp.coffee b/packages/rocketchat-oauth2-server-config/admin/server/methods/addOAuthApp.coffee index 9215823802ddbd008716d072723c4b48760c7413..21e189664dc64073a013c4ba2f34a0bffe6a9c72 100644 --- a/packages/rocketchat-oauth2-server-config/admin/server/methods/addOAuthApp.coffee +++ b/packages/rocketchat-oauth2-server-config/admin/server/methods/addOAuthApp.coffee @@ -15,6 +15,9 @@ Meteor.methods if application.redirectUri.trim() is '' throw new Meteor.Error 'invalid_redirectUri', '[methods] addOAuthApp -> redirectUri can\'t be empty' + if not _.isBoolean(application.active) + throw new Meteor.Error 'invalid_active', '[methods] addOAuthApp -> active must be boolean' + application.clientId = Random.id() application.clientSecret = Random.secret() application._createdAt = new Date diff --git a/packages/rocketchat-oauth2-server-config/admin/server/methods/updateOAuthApp.coffee b/packages/rocketchat-oauth2-server-config/admin/server/methods/updateOAuthApp.coffee index 6cfd200145c115d31083fb9425bb700a8b71a3b7..e50cbf1161104b80fefb23db0ed376b62c32e649 100644 --- a/packages/rocketchat-oauth2-server-config/admin/server/methods/updateOAuthApp.coffee +++ b/packages/rocketchat-oauth2-server-config/admin/server/methods/updateOAuthApp.coffee @@ -15,6 +15,9 @@ Meteor.methods if application.redirectUri.trim() is '' throw new Meteor.Error 'invalid_redirectUri', '[methods] updateOAuthApp -> redirectUri can\'t be empty' + if not _.isBoolean(application.active) + throw new Meteor.Error 'invalid_active', '[methods] updateOAuthApp -> active must be boolean' + currentApplication = RocketChat.models.OAuthApps.findOne(applicationId) if not currentApplication? throw new Meteor.Error 'invalid_application', '[methods] updateOAuthApp -> application not found' @@ -22,6 +25,7 @@ Meteor.methods RocketChat.models.OAuthApps.update applicationId, $set: name: application.name + active: application.active redirectUri: application.redirectUri _updatedAt: new Date _updatedBy: RocketChat.models.Users.findOne @userId, {fields: {username: 1}} diff --git a/packages/rocketchat-oauth2-server-config/oauth/server/default-services.coffee b/packages/rocketchat-oauth2-server-config/oauth/server/default-services.coffee new file mode 100644 index 0000000000000000000000000000000000000000..41fee6a9a863d9a6a10a50f5f030d7ec8a011ecc --- /dev/null +++ b/packages/rocketchat-oauth2-server-config/oauth/server/default-services.coffee @@ -0,0 +1,12 @@ +if not RocketChat.models.OAuthApps.findOne('zapier') + RocketChat.models.OAuthApps.insert + _id: 'zapier' + name: 'Zapier' + active: false + clientId: 'zapier' + clientSecret: 'RTK6TlndaCIolhQhZ7_KHIGOKj41RnlaOq_o-7JKwLr' + redirectUri: 'https://zapier.com/dashboard/auth/oauth/return/AppIDAPI/' + _createdAt: new Date + _createdBy: + _id: 'system' + username: 'system' diff --git a/packages/rocketchat-oauth2-server-config/oauth/server/oauth2-server.coffee b/packages/rocketchat-oauth2-server-config/oauth/server/oauth2-server.coffee index 96940151b582fdee3f0f130d1f0a4287b7696864..770cb583682bf87744294daf32b127aaf324bd87 100644 --- a/packages/rocketchat-oauth2-server-config/oauth/server/oauth2-server.coffee +++ b/packages/rocketchat-oauth2-server-config/oauth/server/oauth2-server.coffee @@ -8,12 +8,6 @@ oauth2server = new OAuth2Server WebApp.connectHandlers.use oauth2server.app # JsonRoutes.Middleware.use oauth2server.app -if not oauth2server.model.Clients.findOne() - oauth2server.model.Clients.insert - clientId: 'papers3' - clientSecret: '123' - redirectUri: 'http://localhost:3000/_oauth/rc' - oauth2server.routes.get '/account', oauth2server.oauth.authorise(), (req, res, next) -> user = Meteor.users.findOne req.user.id diff --git a/packages/rocketchat-oauth2-server-config/package.js b/packages/rocketchat-oauth2-server-config/package.js index 479ba8b40b3d2e57e7f0be287e76d97e04fb6cff..310bebaeed5ff25ef2932258b5d7b91387c27e62 100644 --- a/packages/rocketchat-oauth2-server-config/package.js +++ b/packages/rocketchat-oauth2-server-config/package.js @@ -23,6 +23,7 @@ Package.onUse(function(api) { //// OAuth // // Server api.addFiles('oauth/server/oauth2-server.coffee', 'server'); + api.addFiles('oauth/server/default-services.coffee', 'server'); api.addAssets('oauth/client/stylesheets/oauth2.less', 'server'); api.addFiles('oauth/client/stylesheets/load.coffee', 'server'); diff --git a/packages/rocketchat-theme/assets/stylesheets/base.less b/packages/rocketchat-theme/assets/stylesheets/base.less index dd46ff3ebf0f0d44325cd2b59f878c6586a696b8..1a323d7e600854832b0f67165a2578925c331c62 100644 --- a/packages/rocketchat-theme/assets/stylesheets/base.less +++ b/packages/rocketchat-theme/assets/stylesheets/base.less @@ -2516,6 +2516,13 @@ a.github-fork { line-height: 20px; min-height: 40px; + &.highlight { + -webkit-animation: highlight 3s; + -moz-animation: highlight 3s; + -o-animation: highlight 3s; + animation: highlight 3s; + } + .body, .user.user-card-message, .time { -webkit-user-select: text; -moz-user-select: text; @@ -3073,7 +3080,12 @@ a.github-fork { margin-left: 120px; white-space: normal; .calc(width, ~'100% - 120px'); + h3 { + -webkit-user-select: text; + -moz-user-select: text; + -ms-user-select: text; + user-select: text; font-size: 24px; margin-bottom: 8px; line-height: 27px; @@ -3109,6 +3121,10 @@ a.github-fork { } } p { + -webkit-user-select: text; + -moz-user-select: text; + -ms-user-select: text; + user-select: text; line-height: 18px; font-size: 12px; font-weight: 300; diff --git a/packages/rocketchat-ui/lib/RoomHistoryManager.coffee b/packages/rocketchat-ui/lib/RoomHistoryManager.coffee index aae87a8d192c6322c6e70d03482dc408de2fce86..cb79f341e3c8ca7eddd8db37ea21efa9c993d683 100644 --- a/packages/rocketchat-ui/lib/RoomHistoryManager.coffee +++ b/packages/rocketchat-ui/lib/RoomHistoryManager.coffee @@ -107,6 +107,8 @@ unless message?.rid return + instance = Blaze.getView($('.messages-box .wrapper')[0]).templateInstance() + if ChatMessage.findOne message._id wrapper = $('.messages-box .wrapper') msgElement = $("##{message._id}", wrapper) @@ -114,6 +116,15 @@ wrapper.animate({ scrollTop: pos }, 500) + msgElement.addClass('highlight') + + setTimeout -> + messages = wrapper[0] + instance.atBottom = messages.scrollTop >= messages.scrollHeight - messages.clientHeight; + + setTimeout -> + msgElement.removeClass('highlight') + , 3000 else room = getRoom message.rid room.isLoading.set true @@ -134,7 +145,6 @@ if item.t isnt 'command' ChatMessage.upsert {_id: item._id}, item - instance = Blaze.getView($('.messages-box .wrapper')[0]).templateInstance() Meteor.defer -> readMessage.refreshUnreadMark(message.rid, true) RoomManager.updateMentionsMarksOfRoom typeName @@ -144,11 +154,18 @@ wrapper.animate({ scrollTop: pos }, 500) + + msgElement.addClass('highlight') + setTimeout -> room.isLoading.set false - instance.atBottom = !result.moreAfter + messages = wrapper[0] + instance.atBottom = !result.moreAfter && messages.scrollTop >= messages.scrollHeight - messages.clientHeight; , 500 + setTimeout -> + msgElement.removeClass('highlight') + , 3000 room.loaded += result.messages.length room.hasMore.set result.moreBefore room.hasMoreNext.set result.moreAfter diff --git a/packages/rocketchat-ui/lib/collections.coffee b/packages/rocketchat-ui/lib/collections.coffee index 4c77cee3d1a9b58dff58e25c4a97866c291d0773..a1f8bd138791632e94983beebc0f5d9b6b73fa00 100644 --- a/packages/rocketchat-ui/lib/collections.coffee +++ b/packages/rocketchat-ui/lib/collections.coffee @@ -3,3 +3,8 @@ @ChatSubscription = new Meteor.Collection 'rocketchat_subscription' @UserAndRoom = new Meteor.Collection null @CachedChannelList = new Meteor.Collection null + +RocketChat.models.Users = _.extend {}, RocketChat.models.Users, Meteor.users +RocketChat.models.Subscriptions = _.extend {}, RocketChat.models.Subscriptions, @ChatSubscription +RocketChat.models.Rooms = _.extend {}, RocketChat.models.Rooms, @ChatRoom +RocketChat.models.Messages = _.extend {}, RocketChat.models.Messages, @ChatMessage diff --git a/server/lib/accounts.coffee b/server/lib/accounts.coffee index e68658e7dec5e959b11c7372e8120aa7d3c9ddcb..cc51761cb35ec15a4e087db075d9d2d1892d5aae 100644 --- a/server/lib/accounts.coffee +++ b/server/lib/accounts.coffee @@ -86,7 +86,7 @@ Accounts.insertUserDoc = _.wrap Accounts.insertUserDoc, (insertUserDoc, options, else roles.push 'user' - RocketChat.authz.addUsersToRoles(_id, roles) + RocketChat.authz.addUserRoles(_id, roles) RocketChat.callbacks.run 'afterCreateUser', options, user return _id diff --git a/server/methods/createChannel.coffee b/server/methods/createChannel.coffee index c2e8db8ce1da0be0424ea718f7bef65eaed48468..570d85bfa22ad85748d07b0b74a93c721a1b349e 100644 --- a/server/methods/createChannel.coffee +++ b/server/methods/createChannel.coffee @@ -41,9 +41,6 @@ Meteor.methods room = RocketChat.models.Rooms.createWithTypeNameUserAndUsernames 'c', name, user, members, ts: now - # set creator as channel moderator. permission limited to channel by scoping to rid - RocketChat.authz.addUsersToRoles(Meteor.userId(), 'moderator', room._id) - for username in members member = RocketChat.models.Users.findOneByUsername username if not member? @@ -57,6 +54,9 @@ Meteor.methods RocketChat.models.Subscriptions.createWithRoomAndUser room, member, extra + # set creator as channel moderator. permission limited to channel by scoping to rid + RocketChat.authz.addUserRoles(Meteor.userId(), 'moderator', room._id) + Meteor.defer -> RocketChat.callbacks.run 'afterCreateChannel', user, room diff --git a/server/methods/createPrivateGroup.coffee b/server/methods/createPrivateGroup.coffee index 8bfa8304d98b5ba14b8c2d45a376e478f3cf6147..3a0dd23fa4c43d5b92095cd006470c0c5bd835ff 100644 --- a/server/methods/createPrivateGroup.coffee +++ b/server/methods/createPrivateGroup.coffee @@ -34,7 +34,7 @@ Meteor.methods ts: now # set creator as group moderator. permission limited to group by scoping to rid - RocketChat.authz.addUsersToRoles(Meteor.userId(), 'moderator', room._id) + RocketChat.authz.addUserRoles(Meteor.userId(), 'moderator', room._id) for username in members member = RocketChat.models.Users.findOneByUsername(username, { fields: { username: 1 }}) diff --git a/server/methods/removeUserFromRoom.coffee b/server/methods/removeUserFromRoom.coffee index 78a5137fe2a7536ae0bf03b72016241fa3ba1dcb..f45e43a5bab2b11b364cc42a5aaa600184f4bbf0 100644 --- a/server/methods/removeUserFromRoom.coffee +++ b/server/methods/removeUserFromRoom.coffee @@ -18,7 +18,7 @@ Meteor.methods RocketChat.models.Subscriptions.removeByRoomIdAndUserId data.rid, removedUser._id if room.t in [ 'c', 'p' ] - RocketChat.authz.removeUsersFromRoles(removedUser._id; 'moderator', data.rid) + RocketChat.authz.removeUserFromRoles(removedUser._id; 'moderator', data.rid) fromUser = RocketChat.models.Users.findOneById fromId RocketChat.models.Messages.createUserRemovedWithRoomIdAndUser data.rid, removedUser, diff --git a/server/startup/initialData.coffee b/server/startup/initialData.coffee index 47ba296a93f53a23843ae99c2b7a8f3aa9ee0b25..045b6d0673a8f0ed61df01526010d774b2f8470d 100644 --- a/server/startup/initialData.coffee +++ b/server/startup/initialData.coffee @@ -41,7 +41,7 @@ Meteor.startup -> name: 'Admin' Accounts.setPassword id, process.env.ADMIN_PASS - RocketChat.authz.addUsersToRoles( id, 'admin') + RocketChat.authz.addUserRoles( id, 'admin') else console.log 'E-mail exists; ignoring environment variables ADMIN_EMAIL and ADMIN_PASS'.red @@ -55,5 +55,5 @@ Meteor.startup -> # get oldest user oldestUser = RocketChat.models.Users.findOne({ _id: { $ne: 'rocket.cat' }}, { fields: { username: 1 }, sort: {createdAt: 1}}) if oldestUser - RocketChat.authz.addUsersToRoles( oldestUser._id, 'admin') + RocketChat.authz.addUserRoles( oldestUser._id, 'admin') console.log "No admins are found. Set #{oldestUser.username} as admin for being the oldest user" diff --git a/server/startup/migrations/v27.coffee b/server/startup/migrations/v27.coffee new file mode 100644 index 0000000000000000000000000000000000000000..b3f6f7d7716b880f49b67110bb1557bd8ddf8021 --- /dev/null +++ b/server/startup/migrations/v27.coffee @@ -0,0 +1,11 @@ +Meteor.startup -> + Migrations.add + version: 27 + up: -> + RocketChat.models.Users.update({}, { $rename: { roles: '_roles' } }, { multi: true }) + + RocketChat.models.Users.find({ _roles: { $exists: 1 } }).forEach (user) -> + for scope, roles of user._roles + RocketChat.models.Roles.addUserRoles(user._id, roles, scope) + + RocketChat.models.Users.update({}, { $unset: { _roles: 1 } }, { multi: true })