diff options
Diffstat (limited to 'rbot/auth.rb')
-rw-r--r-- | rbot/auth.rb | 199 |
1 files changed, 199 insertions, 0 deletions
diff --git a/rbot/auth.rb b/rbot/auth.rb new file mode 100644 index 00000000..017745ab --- /dev/null +++ b/rbot/auth.rb @@ -0,0 +1,199 @@ +module Irc + + # globmask:: glob to test with + # netmask:: netmask to test against + # Compare a netmask with a standard IRC glob, e.g foo!bar@baz.com would + # match *!*@baz.com, foo!*@*, *!bar@*, etc. + def Irc.netmaskmatch(globmask, netmask) + regmask = globmask.gsub(/\*/, ".*?") + return true if(netmask =~ /#{regmask}/) + return false + end + + # check if a string is an actual IRC hostmask + def Irc.ismask(mask) + mask =~ /^.+!.+@.+$/ + end + + + # User-level authentication to allow/disallow access to bot commands based + # on hostmask and userlevel. + class IrcAuth + # create a new IrcAuth instance. + # bot:: associated bot class + def initialize(bot) + @bot = bot + @users = Hash.new(0) + @levels = Hash.new(0) + if(File.exist?("#{@bot.botclass}/users.rbot")) + IO.foreach("#{@bot.botclass}/users.rbot") do |line| + if(line =~ /\s*(\d+)\s*(\S+)/) + level = $1.to_i + mask = $2 + @users[mask] = level + end + end + end + if(File.exist?("#{@bot.botclass}/levels.rbot")) + IO.foreach("#{@bot.botclass}/levels.rbot") do |line| + if(line =~ /\s*(\d+)\s*(\S+)/) + level = $1.to_i + command = $2 + @levels[command] = level + end + end + end + end + + # save current users and levels to files. + # levels are written to #{botclass}/levels.rbot + # users are written to #{botclass}/users.rbot + def save + Dir.mkdir("#{@bot.botclass}") if(!File.exist?("#{@bot.botclass}")) + File.open("#{@bot.botclass}/users.rbot", "w") do |file| + @users.each do |key, value| + file.puts "#{value} #{key}" + end + end + File.open("#{@bot.botclass}/levels.rbot", "w") do |file| + @levels.each do |key, value| + file.puts "#{value} #{key}" + end + end + end + + # command:: command user wishes to perform + # mask:: hostmask of user + # tell:: optional recipient for "insufficient auth" message + # + # returns true if user with hostmask +mask+ is permitted to perform + # +command+ optionally pass tell as the target for the "insufficient auth" + # message, if the user is not authorised + def allow?(command, mask, tell=nil) + auth = userlevel(mask) + if(auth >= @levels[command]) + return true + else + debug "#{mask} is not allowed to perform #{command}" + @bot.say tell, "insufficient \"#{command}\" auth (have #{auth}, need #{@levels[command]})" if tell + return false + end + end + + # add user with hostmask matching +mask+ with initial auth level +level+ + def useradd(mask, level) + if(Irc.ismask(mask)) + @users[mask] = level + end + end + + # mask:: mask of user to remove + # remove user with mask +mask+ + def userdel(mask) + if(Irc.ismask(mask)) + @users.delete(mask) + end + end + + # command:: command to adjust + # level:: new auth level for the command + # set required auth level of +command+ to +level+ + def setlevel(command, level) + @levels[command] = level + end + + # specific users. + # mask:: mask of user + # returns the authlevel of user with mask +mask+ + # finds the matching user which has the highest authlevel (so you can have + # a default level of 5 for *!*@*, and yet still give higher levels to + def userlevel(mask) + # go through hostmask list, find match with _highest_ level (all users + # will match *!*@*) + level = 0 + @users.each {|user,userlevel| + if(Irc.netmaskmatch(user, mask)) + level = userlevel if userlevel > level + end + } + level + end + + # return all currently defined commands (for which auth is required) and + # their required authlevels + def showlevels + reply = "Current levels are:" + @levels.sort.each {|a| + key = a[0] + value = a[1] + reply += " #{key}(#{value})" + } + reply + end + + # return all currently defined users and their authlevels + def showusers + reply = "Current users are:" + @users.sort.each {|a| + key = a[0] + value = a[1] + reply += " #{key}(#{value})" + } + reply + end + + # module help + def help(topic="") + case topic + when "setlevel" + return "setlevel <command> <level> => Sets required level for <command> to <level> (private addressing only)" + when "useradd" + return "useradd <mask> <level> => Add user <mask> at level <level> (private addressing only)" + when "userdel" + return "userdel <mask> => Remove user <mask> (private addressing only)" + when "auth" + return "auth <masterpw> => Recognise your hostmask as bot master (private addressing only)" + when "levels" + return "levels => list commands and their required levels (private addressing only)" + when "users" + return "users => list users and their levels (private addressing only)" + else + return "Auth module (User authentication) topics: setlevel, useradd, userdel, auth, levels, users" + end + end + + # privmsg handler + def privmsg(m) + if(m.address? && m.private?) + case m.message + when (/^setlevel\s+(\S+)\s+(\d+)$/) + if(@bot.auth.allow?("auth", m.source, m.replyto)) + @bot.auth.setlevel($1, $2.to_i) + m.reply "level for #$1 set to #$2" + end + when (/^useradd\s+(\S+)\s+(\d+)/) + if(@bot.auth.allow?("auth", m.source, m.replyto)) + @bot.auth.useradd($1, $2.to_i) + m.reply "added user #$1 at level #$2" + end + when (/^userdel\s+(\S+)/) + if(@bot.auth.allow?("auth", m.source, m.replyto)) + @bot.auth.userdel($1) + m.reply "user #$1 is gone" + end + when (/^auth\s+(\S+)/) + if($1 == @bot.config["PASSWD"]) + @bot.auth.useradd(Regexp.escape(m.source), 1000) + m.reply "Identified, security level maxed out" + else + m.reply "incorrect password" + end + when ("levels") + m.reply @bot.auth.showlevels if(@bot.auth.allow?("config", m.source, m.replyto)) + when ("users") + m.reply @bot.auth.showusers if(@bot.auth.allow?("config", m.source, m.replyto)) + end + end + end + end +end |