Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve roles persistency #40

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
114 changes: 65 additions & 49 deletions src/auth.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
# * Call the method: robot.auth.hasRole(msg.envelope.user,'<role>')
# * returns bool true or false
#
# * the 'admin' role can only be assigned through the environment variable
# * the 'admin' role can only be assigned through the environment variable and
# it is not persisted
# * roles are all transformed to lower case
#
# * The script assumes that user IDs will be unique on the service end as to
Expand All @@ -28,43 +29,25 @@ config =
role_list: process.env.HUBOT_AUTH_ROLES

module.exports = (robot) ->
class Auth

# TODO: This has been deprecated so it needs to be removed at some point.
if config.admin_list?
robot.logger.warning 'The HUBOT_AUTH_ADMIN environment variable has been deprecated in favor of HUBOT_AUTH_ROLES'
for id in config.admin_list.split ','
user = robot.brain.userForId id
# admin role is not persistent. List of user IDs who have admin role
admins = []

unless user?
robot.logger.warning "#{id} does not exist"
else
user.roles or= []
user.roles.push 'admin' unless 'admin' in user.roles
fetchAllRoles: () ->
unless robot.brain.get('roles')
robot.brain.set('roles', {})
robot.brain.get('roles')

unless config.role_list?
robot.logger.warning 'The HUBOT_AUTH_ROLES environment variable not set'
else
for role in config.role_list.split ' '
[dummy, roleName, userIds] = role.match /(\w+)=([\w]+(?:,[\w]+)*)/
for id in userIds.split ','
user = robot.brain.userForId id

unless user?
robot.logger.warning "#{id} does not exist"
else
user.roles or= []
user.roles.push roleName unless roleName in user.roles

class Auth
isAdmin: (user) ->
roles = robot.brain.userForId(user.id).roles or []
'admin' in roles
@hasRole(user, 'admin')

hasRole: (user, roles) ->
userRoles = @userRoles(user)
if userRoles?
roles = [roles] if typeof roles is 'string'
for role in roles
return true if role == "admin" and user.id in admins
return true if role in userRoles
return false

Expand All @@ -76,10 +59,49 @@ module.exports = (robot) ->
users

userRoles: (user) ->
user.roles
@fetchAllRoles()[user.id] or []

addRole: (user, newRole) ->
if newRole == "admin"
admins.push(user.id)
return
userRoles = @userRoles(user)
userRoles.push newRole unless newRole in userRoles
allNewRoles = @fetchAllRoles()
allNewRoles[user.id] = userRoles
robot.brain.set('roles', allNewRoles)

revokeRole: (user, newRole) ->
if role == "admin"
admins = (u for u in admins when u != user.id)
return
userRoles = @userRoles(user)
userRoles = (role for role in userRoles when role isnt newRole)
allNewRoles = @fetchAllRoles()
allNewRoles[user.id] = userRoles
robot.brain.set('roles', allNewRoles)

getRoles: () ->
result = if admins then ["admin"] else []
for own key,roles of @fetchAllRoles('roles')
result.push role for role in roles unless role in result
result

robot.auth = new Auth

# TODO: This has been deprecated so it needs to be removed at some point.
if config.admin_list?
robot.logger.warning 'The HUBOT_AUTH_ADMIN environment variable has been deprecated in favor of HUBOT_AUTH_ROLES'
for id in config.admin_list.split ','
robot.auth.addRole({ id }, 'admin')

unless config.role_list?
robot.logger.warning 'The HUBOT_AUTH_ROLES environment variable not set'
else
for role in config.role_list.split ' '
[dummy, roleName, userIds] = role.match /(\w+)=([\w]+(?:,[\w]+)*)/
for id in userIds.split ','
robot.auth.addRole({ id }, roleName)

robot.respond /@?([^\s]+) ha(?:s|ve) (["'\w: -_]+) role/i, (msg) ->
name = msg.match[1].trim()
Expand All @@ -93,17 +115,14 @@ module.exports = (robot) ->

user = robot.brain.userForName(name)
return msg.reply "#{name} does not exist" unless user?
user.roles or= []

if newRole in user.roles
if robot.auth.hasRole(user, newRole)
msg.reply "#{name} already has the '#{newRole}' role."
else if newRole is 'admin'
msg.reply "Sorry, the 'admin' role can only be defined in the HUBOT_AUTH_ROLES env variable."
else
if newRole is 'admin'
msg.reply "Sorry, the 'admin' role can only be defined in the HUBOT_AUTH_ROLES env variable."
else
myRoles = msg.message.user.roles or []
user.roles.push(newRole)
msg.reply "OK, #{name} has the '#{newRole}' role."
robot.auth.addRole user, newRole
msg.reply "OK, #{name} has the '#{newRole}' role."

robot.respond /@?([^\s]+) (?:don['’]t|doesn['’]t|do not) have (["'\w: -_]+) role/i, (msg) ->
name = msg.match[1].trim()
Expand All @@ -117,21 +136,20 @@ module.exports = (robot) ->

user = robot.brain.userForName(name)
return msg.reply "#{name} does not exist" unless user?
user.roles or= []

if newRole is 'admin'
msg.reply "Sorry, the 'admin' role can only be removed from the HUBOT_AUTH_ROLES env variable."
else
myRoles = msg.message.user.roles or []
user.roles = (role for role in user.roles when role isnt newRole)
robot.auth.revokeRole user, newRole
msg.reply "OK, #{name} doesn't have the '#{newRole}' role."

robot.respond /what roles? do(es)? @?([^\s]+) have\?*$/i, (msg) ->
name = msg.match[2].trim()
if name.toLowerCase() is 'i' then name = msg.message.user.name
user = robot.brain.userForName(name)
return msg.reply "#{name} does not exist" unless user?
userRoles = robot.auth.userRoles(user)
userRoles = (x for x in robot.auth.userRoles(user))
userRoles.unshift("admin") if robot.auth.isAdmin(user)

if userRoles.length == 0
msg.reply "#{name} has no roles."
Expand All @@ -148,13 +166,11 @@ module.exports = (robot) ->
msg.reply "There are no people that have the '#{role}' role."

robot.respond /list assigned roles/i, (msg) ->
roles = []
unless robot.auth.isAdmin msg.message.user
msg.reply "Sorry, only admins can list assigned roles."
msg.reply "Sorry, only admins can list assigned roles."
else
for i, user of robot.brain.users() when user.roles
roles.push role for role in user.roles when role not in roles
if roles.length > 0
msg.reply "The following roles are available: #{roles.join(', ')}"
else
msg.reply "No roles to list."
roles = robot.auth.getRoles()
if roles.length > 0
msg.reply "The following roles are available: #{roles.join(', ')}"
else
msg.reply "No roles to list."