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