Skip to content
Snippets Groups Projects
Commit 599c6508 authored by Rodrigo Nascimento's avatar Rodrigo Nascimento
Browse files

Merge branch 'master' into models

# Conflicts:
#	server/lib/accounts.coffee
#	server/methods/deleteMessage.coffee
#	server/methods/deleteUser.coffee
#	server/methods/eraseRoom.coffee
#	server/methods/setUserActiveStatus.coffee
#	server/methods/updateMessage.coffee
#	server/publications/adminRooms.coffee
#	server/publications/fullUserData.coffee
#	server/publications/userChannels.coffee
#	server/startup/initialData.coffee
parents f17ea174 a134a870
No related merge requests found
Showing
with 246 additions and 11 deletions
Package.describe({
name: 'rocketchat:authorization',
version: '0.0.1',
summary: 'Role based authorization of actions',
git: '',
documentation: 'README.md'
});
Package.onUse(function(api) {
api.versionsFrom('1.0');
api.use([
'coffeescript',
'rocketchat:lib@0.0.1',
'alanning:roles@1.2.12'
]);
api.use('templating', 'client');
api.addFiles('lib/permissions.coffee', ['server', 'client']);
api.addFiles('lib/rocketchat.coffee', ['server','client']);
api.addFiles('client/startup.coffee', ['client']);
api.addFiles('client/hasPermission.coffee', ['client']);
api.addFiles('client/hasRole.coffee', ['client']);
api.addFiles('server/functions/addUsersToRoles.coffee', ['server']);
api.addFiles('server/functions/getPermissionsForRole.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/publication.coffee', ['server']);
api.addFiles('server/startup.coffee', ['server']);
});
RocketChat.authz.addUsersToRoles = (userIds, roleNames, scope ) ->
console.log '[methods] addUserToRoles -> '.green, 'arguments:', arguments
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.addUsersToRoles( userIds, roleNames, scope)
return true
\ No newline at end of file
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'
return _.pluck(ChatPermissions.find({roles : roleName }).fetch(), '_id')
\ No newline at end of file
RocketChat.authz.getRoles = ->
return Roles.getAllRoles()
\ No newline at end of file
RocketChat.authz.getRolesForUser = (userId, scope) ->
console.log '[methods] getRolesForUser -> '.green, 'arguments:', arguments
# returns roles for the given scope as well as the global scope
unless scope
scope = Roles.GLOBAL_GROUP
return Roles.getRolesForUser(userId, scope)
\ No newline at end of file
RocketChat.authz.getUsersInRole = (roleName, scope) ->
# alanning:roles doc says this is an expensive operation
unless _.isString(scope)
scope = Roles.GLOBAL_GROUP
return Roles.getUsersInRole(roleName, scope)
\ No newline at end of file
RocketChat.authz.hasPermission = (userId, permissionId, scope) ->
console.log '[methods] hasPermission -> '.green, 'arguments:', arguments
# 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
\ No newline at end of file
RocketChat.authz.hasRole = (userId, roleName, scope) ->
console.log '[methods] hasRoles -> '.green, 'arguments:', arguments
# per alanning:roles, returns true if user is in ANY roles
return Roles.userIsInRole(userId, [roleName], scope)
\ No newline at end of file
RocketChat.authz.removeUsersFromRoles = (userIds, roleNames, scope ) ->
console.log '[methods] removeUsersFromRoles -> '.green, 'arguments:', arguments
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
\ No newline at end of file
Meteor.publish 'permissions', ->
console.log '[publish] permissions'.green
return ChatPermissions.find {}
\ No newline at end of file
Meteor.startup ->
# Note:
# 1.if we need to create a role that can only edit channel message, but not edit group message
# then we can define edit-<type>-message instead of edit-message
# 2. admin, moderator, and user roles should not be deleted as they are referened in the code.
permissions = [
{ _id: 'view-statistics',
roles : ['admin', 'temp-role']}
{ _id: 'view-privileged-setting',
roles : ['admin']}
{ _id: 'edit-privileged-setting',
roles : ['admin']}
{ _id: 'view-room-administration',
roles : ['admin']}
{ _id: 'view-user-administration',
roles : ['admin']}
{ _id: 'view-full-other-user-info',
roles : ['admin']}
{ _id: 'edit-other-user-info',
roles : ['admin']}
{ _id: 'assign-admin-role',
roles : ['admin']}
{ _id: 'edit-other-user-active-status',
roles : ['admin', 'site-moderator']}
{ _id: 'delete-user',
roles : ['admin']}
{ _id: 'view-other-user-channels',
roles : ['admin']}
{ _id: 'add-oath-service',
roles : ['admin']}
{ _id: 'run-migration',
roles : ['admin']}
{ _id: 'create-c',
roles : ['admin', 'site-moderator', 'user']}
{ _id: 'delete-c',
roles : ['admin', 'site-moderator']}
{ _id: 'edit-room',
roles : ['admin', 'site-moderator', 'moderator']}
{ _id: 'edit-message',
roles : ['admin', 'site-moderator', 'moderator']}
{ _id: 'delete-message',
roles : ['admin', 'site-moderator', 'moderator']}
{ _id: 'ban-user',
roles : ['admin', 'site-moderator', 'moderator']}
{ _id: 'create-p',
roles : ['admin', 'site-moderator', 'user']}
{ _id: 'delete-p',
roles : ['admin', 'site-moderator']}
{ _id: 'delete-d',
roles : ['admin', 'site-moderator']}
]
#alanning:roles
roles = _.pluck(Roles.getAllRoles().fetch(), 'name');
for permission in permissions
ChatPermissions.upsert( permission._id, {$setOnInsert : permission })
for role in permission.roles
unless role in roles
Roles.createRole role
roles.push(role)
......@@ -3,9 +3,12 @@ Meteor.methods
if not Meteor.userId()
throw new Meteor.Error 'invalid-user', "[methods] setAdminStatus -> Invalid user"
unless Meteor.user()?.admin is true
unless RocketChat.authz.hasPermission( Meteor.userId(), 'assign-admin-role') is true
throw new Meteor.Error 'not-authorized', '[methods] setAdminStatus -> Not authorized'
Meteor.users.update userId, { $set: { admin: admin } }
if admin
RocketChat.authz.addUsersToRoles( userId, 'admin')
else
RocketChat.authz.removeUsersFromRoles( userId, 'admin')
return true
......@@ -7,7 +7,8 @@ Meteor.methods
user = Meteor.user()
if user._id isnt userData._id and user.admin isnt true
canEditUserPermission = RocketChat.authz.hasPermission( user._id, 'edit-other-user-info')
if user._id isnt userData._id and canEditUserPermission isnt true
throw new Meteor.Error 'not-authorized', '[methods] updateUser -> Not authorized'
unless userData._id
......
......@@ -5,7 +5,7 @@ Meteor.methods
console.log '[methods] addOAuthService -> '.green, 'userId:', Meteor.userId(), 'arguments:', arguments
unless Meteor.user()?.admin is true
unless RocketChat.authz.hasALeastOnePermission( Meteor.userId(), 'add-oath-service') is true
throw new Meteor.Error 'not-authorized', '[methods] addOAuthService -> Not authorized'
name = s.capitalize(name)
......
......@@ -50,7 +50,7 @@ Meteor.methods
if Meteor.userId()?
user = Meteor.users.findOne Meteor.userId()
unless user?.admin is true
unless RocketChat.authz.hasPermission(Meteor.userId(), 'edit-privileged-setting') is true
throw new Meteor.Error 503, 'Not authorized'
# console.log "saveSetting -> ".green, _id, value
......
......@@ -8,8 +8,7 @@ Meteor.publish 'admin-settings', ->
unless @userId
return @ready()
user = Meteor.users.findOne @userId
if user.admin
if RocketChat.authz.hasPermission( @userId, 'view-privileged-setting')
return Settings.find()
else
return @ready()
......
......@@ -5,7 +5,7 @@ Meteor.methods
console.log '[methods] getStatistics -> '.green, 'userId:', Meteor.userId(), 'arguments:', arguments
unless Meteor.user()?.admin is true
unless RocketChat.authz.hasPermission(Meteor.userId(), 'view-statistics') is true
throw new Meteor.Error 'not-authorized', '[methods] getStatistics -> Not authorized'
return RocketChat.statistics.get()
\ No newline at end of file
......@@ -24,9 +24,8 @@ Accounts.onCreateUser (options, user) ->
user.status = 'offline'
user.active = not RocketChat.settings.get 'Accounts_ManuallyApproveNewUsers'
# when inserting first user, set admin: true
unless RocketChat.models.Users.findOne()
user.admin = true
# when inserting first user give them admin privileges otherwise make a regular user
roleName = if RocketChat.models.Users.findOne() then 'user' else 'admin'
if not user?.name? or user.name is ''
if options.profile?.name?
......@@ -47,6 +46,9 @@ Accounts.onCreateUser (options, user) ->
]
Meteor.defer ->
# need to defer role assignment because underlying alanning:roles requires user
# to exist in users collection
RocketChat.authz.addUsersToRoles( user._id, roleName)
RocketChat.callbacks.run 'afterCreateUser', options, user
return user
......
......@@ -6,6 +6,9 @@ Meteor.methods
if not /^[0-9a-z-_]+$/.test name
throw new Meteor.Error 'name-invalid'
if RocketChat.authz.hasPermission(Meteor.userId(), 'create-c') isnt true
throw new Meteor.Error 'not-authorized', '[methods] createChannel -> Not authorized'
console.log '[methods] createChannel -> '.green, 'userId:', Meteor.userId(), 'arguments:', arguments
now = new Date()
......@@ -33,6 +36,8 @@ Meteor.methods
# create new room
rid = ChatRoom.insert room
# set creator as channel moderator. permission limited to channel by scoping to rid
RocketChat.authz.addUsersToRoles(Meteor.userId(), 'moderator', rid)
for username in members
member = RocketChat.models.Users.findOneByUsername username
......
......@@ -3,6 +3,9 @@ Meteor.methods
if not Meteor.userId()
throw new Meteor.Error 'invalid-user', "[methods] createPrivateGroup -> Invalid user"
unless RocketChat.authz.hasPermission(Meteor.userId(), 'create-p')
throw new Meteor.Error 'not-authorized', '[methods] createPrivateGroup -> Not authorized'
console.log '[methods] createPrivateGroup -> '.green, 'userId:', Meteor.userId(), 'arguments:', arguments
if not /^[0-9a-z-_]+$/.test name
......@@ -31,6 +34,9 @@ Meteor.methods
name: name
msgs: 0
# set creator as group moderator. permission limited to group by scoping to rid
RocketChat.authz.addUsersToRoles(Meteor.userId(), 'moderator', rid)
for username in members
member = RocketChat.models.Users.findOneByUsername(username, { fields: { username: 1 }})
if not member?
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment