]> git.netwichtig.de Git - user/henk/code/ruby/rbot.git/blob - lib/rbot/auth.rb
Sat Jul 30 01:19:32 BST 2005 Tom Gilbert <tom@linuxbrit.co.uk>
[user/henk/code/ruby/rbot.git] / lib / rbot / auth.rb
1 module Irc
2
3   # globmask:: glob to test with
4   # netmask::  netmask to test against
5   # Compare a netmask with a standard IRC glob, e.g foo!bar@baz.com would
6   # match *!*@baz.com, foo!*@*, *!bar@*, etc.
7   def Irc.netmaskmatch(globmask, netmask)
8     regmask = globmask.gsub(/\*/, ".*?")
9     return true if(netmask =~ /#{regmask}/)
10     return false
11   end
12
13   # check if a string is an actual IRC hostmask
14   def Irc.ismask(mask)
15     mask =~ /^.+!.+@.+$/
16   end
17
18   
19   # User-level authentication to allow/disallow access to bot commands based
20   # on hostmask and userlevel.
21   class IrcAuth
22     BotConfig.register('auth.password', :type => BotConfig::Password, :default => "Your password for maxing your auth with the bot (used to associate new hostmasks with your owner-status etc)")
23     
24     # create a new IrcAuth instance.
25     # bot:: associated bot class
26     def initialize(bot)
27       @bot = bot
28       @users = Hash.new(0)
29       @levels = Hash.new(0)
30       if(File.exist?("#{@bot.botclass}/users.rbot"))
31         IO.foreach("#{@bot.botclass}/users.rbot") do |line|
32           if(line =~ /\s*(\d+)\s*(\S+)/)
33             level = $1.to_i
34             mask = $2
35             @users[mask] = level
36           end
37         end
38       end
39       if(File.exist?("#{@bot.botclass}/levels.rbot"))
40         IO.foreach("#{@bot.botclass}/levels.rbot") do |line|
41           if(line =~ /\s*(\d+)\s*(\S+)/)
42             level = $1.to_i
43             command = $2
44             @levels[command] = level
45           end
46         end
47       end
48     end
49
50     # save current users and levels to files.
51     # levels are written to #{botclass}/levels.rbot
52     # users are written to #{botclass}/users.rbot
53     def save
54       Dir.mkdir("#{@bot.botclass}") if(!File.exist?("#{@bot.botclass}"))
55       File.open("#{@bot.botclass}/users.rbot", "w") do |file|
56         @users.each do |key, value|
57           file.puts "#{value} #{key}"
58         end
59       end
60       File.open("#{@bot.botclass}/levels.rbot", "w") do |file|
61         @levels.each do |key, value|
62           file.puts "#{value} #{key}"
63         end
64       end
65     end
66
67     # command:: command user wishes to perform
68     # mask::    hostmask of user
69     # tell::    optional recipient for "insufficient auth" message
70     #
71     # returns true if user with hostmask +mask+ is permitted to perform
72     # +command+ optionally pass tell as the target for the "insufficient auth"
73     # message, if the user is not authorised
74     def allow?(command, mask, tell=nil)
75       auth = userlevel(mask)
76       if(auth >= @levels[command])
77         return true
78       else
79         debug "#{mask} is not allowed to perform #{command}"
80         @bot.say tell, "insufficient \"#{command}\" auth (have #{auth}, need #{@levels[command]})" if tell
81         return false
82       end
83     end
84
85     # add user with hostmask matching +mask+ with initial auth level +level+
86     def useradd(mask, level)
87       if(Irc.ismask(mask))
88         @users[mask] = level
89       end
90     end
91     
92     # mask:: mask of user to remove
93     # remove user with mask +mask+
94     def userdel(mask)
95       if(Irc.ismask(mask))
96         @users.delete(mask)
97       end
98     end
99
100     # command:: command to adjust
101     # level::   new auth level for the command
102     # set required auth level of +command+ to +level+
103     def setlevel(command, level)
104       @levels[command] = level
105     end
106
107     # specific users.
108     # mask:: mask of user
109     # returns the authlevel of user with mask +mask+
110     # finds the matching user which has the highest authlevel (so you can have
111     # a default level of 5 for *!*@*, and yet still give higher levels to
112     def userlevel(mask)
113       # go through hostmask list, find match with _highest_ level (all users
114       # will match *!*@*)
115       level = 0
116       @users.each {|user,userlevel|
117         if(Irc.netmaskmatch(user, mask))
118           level = userlevel if userlevel > level
119         end
120       }
121       level
122     end
123
124     # return all currently defined commands (for which auth is required) and
125     # their required authlevels
126     def showlevels
127       reply = "Current levels are:"
128       @levels.sort.each {|a|
129         key = a[0]
130         value = a[1]
131         reply += " #{key}(#{value})"
132       }
133       reply
134     end
135
136     # return all currently defined users and their authlevels
137     def showusers
138       reply = "Current users are:"
139       @users.sort.each {|a|
140         key = a[0]
141         value = a[1]
142         reply += " #{key}(#{value})"
143       }
144       reply
145     end
146     
147     # module help
148     def help(topic="")
149       case topic
150         when "setlevel"
151           return "setlevel <command> <level> => Sets required level for <command> to <level> (private addressing only)"
152         when "useradd"
153           return "useradd <mask> <level> => Add user <mask> at level <level> (private addressing only)"
154         when "userdel"
155           return "userdel <mask> => Remove user <mask> (private addressing only)"
156         when "auth"
157           return "auth <masterpw> => Recognise your hostmask as bot master (private addressing only)"
158         when "levels"
159           return "levels => list commands and their required levels (private addressing only)"
160         when "users"
161           return "users => list users and their levels (private addressing only)"
162         else
163           return "Auth module (User authentication) topics: setlevel, useradd, userdel, auth, levels, users"
164       end
165     end
166
167     # privmsg handler
168     def privmsg(m)
169      if(m.address? && m.private?)
170       case m.message
171         when (/^setlevel\s+(\S+)\s+(\d+)$/)
172           if(@bot.auth.allow?("auth", m.source, m.replyto))
173             @bot.auth.setlevel($1, $2.to_i)
174             m.reply "level for #$1 set to #$2"
175           end
176         when (/^useradd\s+(\S+)\s+(\d+)/)
177           if(@bot.auth.allow?("auth", m.source, m.replyto))
178             @bot.auth.useradd($1, $2.to_i)
179             m.reply "added user #$1 at level #$2"
180           end
181         when (/^userdel\s+(\S+)/)
182           if(@bot.auth.allow?("auth", m.source, m.replyto))
183             @bot.auth.userdel($1)
184             m.reply "user #$1 is gone"
185           end
186         when (/^auth\s+(\S+)/)
187           if($1 == @bot.config["auth.password"])
188             @bot.auth.useradd(Regexp.escape(m.source), 1000)
189             m.reply "Identified, security level maxed out"
190           else
191             m.reply "incorrect password"
192           end
193         when ("levels")
194           m.reply @bot.auth.showlevels if(@bot.auth.allow?("config", m.source, m.replyto))
195         when ("users")
196           m.reply @bot.auth.showusers if(@bot.auth.allow?("config", m.source, m.replyto))
197       end
198      end
199     end
200   end
201 end