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