5 class AuthModule < CoreBotModule
\r
9 load_array(:default, true)
\r
10 debug "Initialized auth. Botusers: #{@bot.auth.save_array.inspect}"
\r
17 def save_array(key=:default)
\r
18 if @bot.auth.changed?
\r
19 @registry[key] = @bot.auth.save_array
\r
20 @bot.auth.reset_changed
\r
21 debug "saved botusers (#{key}): #{@registry[key].inspect}"
\r
25 def load_array(key=:default, forced=false)
\r
26 debug "loading botusers (#{key}): #{@registry[key].inspect}"
\r
27 @bot.auth.load_array(@registry[key], forced) if @registry.has_key?(key)
\r
30 # The permission parameters accept arguments with the following syntax:
\r
31 # cmd_path... [on #chan .... | in here | in private]
\r
32 # This auxiliary method scans the array _ar_ to see if it matches
\r
33 # the given syntax: it expects + or - signs in front of _cmd_path_
\r
34 # elements when _setting_ = true
\r
36 # It returns an array whose first element is the array of cmd_path,
\r
37 # the second element is an array of locations and third an array of
\r
38 # warnings occurred while parsing the strings
\r
40 def parse_args(ar, setting)
\r
45 next_must_be_chan = false
\r
48 ar.each_with_index { |x, i|
\r
49 if doing_cmds # parse cmd_path
\r
50 # check if the list is done
\r
51 if x == "on" or x == "in"
\r
53 next_must_be_chan = true if x == "on"
\r
56 if "+-".include?(x[0])
\r
57 warns << ArgumentError("please do not use + or - in front of command #{x} when resetting") unless setting
\r
59 warns << ArgumentError("+ or - expected in front of #{x}") if setting
\r
62 else # parse locations
\r
68 case next_must_be_chan
\r
70 locs << x.gsub(/^here$/,'_').gsub(/^private$/,'?')
\r
72 warns << ArgumentError("#{x} doesn't look like a channel name") unless @bot.server.supports[:chantypes].include?(x[0])
\r
81 warns << "trailing comma" if wants_more
\r
82 warns << "you probably forgot a comma" unless last_idx == ar.length - 1
\r
83 return cmds, locs, warns
\r
86 def auth_set(m, params)
\r
87 cmds, locs, warns = parse_args(params[:args])
\r
88 errs = warns.select { |w| w.kind_of?(Exception) }
\r
90 m.reply "couldn't satisfy your request: #{errs.join(',')}"
\r
93 user = params[:user].sub(/^all$/,"everyone")
\r
95 bu = @bot.auth.get_botuser(user)
\r
97 m.reply "couldn't find botuser #{user}"
\r
107 ch = "?" if loc == "_"
\r
109 ch = m.target.to_s if loc == "_"
\r
111 cmds.each { |setval|
\r
112 val = setval[0].chr == '+'
\r
113 cmd = setval[1..-1]
\r
114 bu.set_permission(cmd, val, ch)
\r
118 m.reply "Something went wrong while trying to set the permissions"
\r
121 @bot.auth.set_changed
\r
122 debug "User #{user} permissions changed"
\r
123 m.reply "Ok, #{user} now also has permissions #{params[:args].join(' ')}"
\r
126 def get_botuser_for(user)
\r
127 @bot.auth.irc_to_botuser(user)
\r
130 def get_botusername_for(user)
\r
131 get_botuser_for(user).username
\r
135 "welcome, #{get_botusername_for(user)}"
\r
138 def auth_login(m, params)
\r
140 case @bot.auth.login(m.source, params[:botuser], params[:password])
\r
142 m.reply welcome(m.source)
\r
143 @bot.auth.set_changed
\r
145 m.reply "sorry, can't do"
\r
148 m.reply "couldn't login: #{e}"
\r
153 def auth_autologin(m, params)
\r
154 u = do_autologin(m.source)
\r
157 m.reply "I couldn't find anything to let you login automatically"
\r
159 m.reply welcome(m.source)
\r
163 def do_autologin(user)
\r
164 @bot.auth.autologin(user)
\r
167 def auth_whoami(m, params)
\r
170 # rep << m.source.nick << ", "
\r
173 rep << get_botusername_for(m.source).gsub(/^everyone$/, "no one that I know").gsub(/^owner$/, "my boss")
\r
177 def help(plugin, topic="")
\r
180 return "login [<botuser>] [<pass>]: logs in to the bot as botuser <botuser> with password <pass>. <pass> can be omitted if <botuser> allows login-by-mask and your netmask is among the known ones. if <botuser> is omitted too autologin will be attempted"
\r
182 return "whoami: names the botuser you're linked to"
\r
183 when /^permission syntax/
\r
184 return "A permission is specified as module::path::to::cmd; when you want to enable it, prefix it with +; when you want to disable it, prefix it with -; when using the +reset+ command, do not use any prefix"
\r
186 return "permissions (re)set <permission> [in <channel>] for <user>: sets or resets the permissions for botuser <user> in channel <channel> (use ? to change the permissions for private addressing)"
\r
187 when /^user (show|list)/
\r
188 return "user show <what> : shows info about the user; <what> can be any of autologin, login-by-mask, netmasks"
\r
189 when /^user (en|dis)able/
\r
190 return "user enable|disable <what> : turns on or off <what> (autologin, login-by-mask)"
\r
192 return "user set password <blah> : sets the user password to <blah>; passwords can only contain upper and lowercase letters and numbers, and must be at least 4 characters long"
\r
193 when /^user (add|rm)/
\r
194 return "user add|rm netmask <mask> : adds/removes netmask <mask> from the list of netmasks known to the botuser you're linked to"
\r
196 return "user reset <what> : resets <what> to the default values. <what> can be +netmasks+ (the list will be emptied), +autologin+ or +login-by-mask+ (will be reset to the default value) or +password+ (a new one will be generated and you'll be told in private)"
\r
198 return "user tell <who> the password for <botuser> : contacts <who> in private to tell him/her the password for <botuser>"
\r
200 return "user show|list, enable|disable, add|rm netmask, set, reset, tell"
\r
202 return "#{name}: login, whoami, permission syntax, permissions, user"
\r
207 "sorry, I need more arguments to #{cmd}"
\r
210 def not_args(cmd, *stuff)
\r
211 "I can only #{cmd} these: #{stuff.join(', ')}"
\r
214 def set_prop(botuser, prop, val)
\r
215 k = prop.to_s.gsub("-","_")
\r
216 botuser.send( (k + "=").to_sym, val)
\r
219 def reset_prop(botuser, prop)
\r
220 k = prop.to_s.gsub("-","_")
\r
221 botuser.send( ("reset_"+k).to_sym)
\r
224 def ask_bool_prop(botuser, prop)
\r
225 k = prop.to_s.gsub("-","_")
\r
226 botuser.send( (k + "?").to_sym)
\r
229 def auth_manage_user(m, params)
\r
230 splits = params[:data]
\r
233 return auth_whoami(m, params) if cmd.nil?
\r
235 botuser = get_botuser_for(m.source)
\r
236 # By default, we do stuff on the botuser the irc user is bound to
\r
239 has_for = splits[-2] == "for"
\r
240 butarget = @bot.auth.get_botuser(splits[-1]) if has_for
\r
241 return m.reply "you can't mess with #{butarget.username}" if butarget == @bot.auth.botowner && botuser != butarget
\r
242 splits.slice!(-2,2) if has_for
\r
244 bools = [:autologin, :"login-by-mask"]
\r
245 can_set = [:password]
\r
246 can_addrm = [:netmasks]
\r
247 can_reset = bools + can_set + can_addrm
\r
252 return "you can't see the properties of #{butarget.username}" if botuser != butarget and !botuser.permit?("auth::show::other")
\r
258 if botuser != butarget
\r
259 return m.reply "no way I'm telling you the master password!" if butarget == @bot.auth.botowner
\r
260 return m.reply "you can't ask for someone else's password"
\r
262 return m.reply "c'mon, you can't be asking me seriously to tell you the password in public!" if m.public?
\r
263 return m.reply "the password for #{butarget.username} is #{butarget.password}"
\r
265 props = splits[1..-1]
\r
272 next if k == :password
\r
276 str.last << "not" unless ask_bool_prop(butarget, k)
\r
277 str.last << " #{k}"
\r
280 if butarget.netmasks.empty?
\r
281 str.last << "no netmasks"
\r
283 str.last << butarget.netmasks.join(", ")
\r
287 return m.reply "#{butarget.username} #{str.join('; ')}"
\r
289 when :enable, :disable
\r
290 return m.reply "you can't change the default user" if butarget == @bot.auth.everyone and !botuser.permit?("auth::edit::other::default")
\r
291 return m.reply "you can't edit #{butarget.username}" if butarget != botuser and !botuser.permit?("auth::edit::other")
\r
293 return m.reply need_args(cmd) unless splits[1]
\r
296 splits[1..-1].each { |a|
\r
298 if bools.include?(arg)
\r
299 set_prop(butarget, arg, cmd.to_sym == :enable)
\r
306 m.reply "I ignored #{skipped.join(', ')} because " + not_args(cmd, *bools) unless skipped.empty?
\r
308 m.reply "I haven't changed anything"
\r
310 @bot.auth.set_changed
\r
311 return auth_manage_user(m, {:data => ["show"] + things })
\r
315 return m.reply "you can't change the default user" if butarget == @bot.auth.everyone and !botuser.permit?("auth::edit::default")
\r
316 return m.reply "you can't edit #{butarget.username}" if butarget != botuser and !botuser.permit?("auth::edit::other")
\r
318 return m.reply need_args(cmd) unless splits[1]
\r
319 arg = splits[1].to_sym
\r
320 return m.reply not_args(cmd, *can_set) unless can_set.include?(arg)
\r
322 return m.reply need_args([cmd, splits[1]].join(" ")) unless argarg
\r
323 if arg == :password && m.public?
\r
324 return m.reply "is that a joke? setting the password in public?"
\r
326 set_prop(butarget, arg, argarg)
\r
327 @bot.auth.set_changed
\r
328 auth_manage_user(m, {:data => ["show", arg] })
\r
331 return m.reply "you can't change the default user" if butarget == @bot.auth.everyone and !botuser.permit?("auth::edit::default")
\r
332 return m.reply "you can't edit #{butarget.username}" if butarget != botuser and !botuser.permit?("auth::edit::other")
\r
334 return m.reply need_args(cmd) unless splits[1]
\r
337 splits[1..-1].each { |a|
\r
339 if can_reset.include?(arg)
\r
340 reset_prop(butarget, arg)
\r
347 m.reply "I ignored #{skipped.join(', ')} because " + not_args(cmd, *can_reset) unless skipped.empty?
\r
349 m.reply "I haven't changed anything"
\r
351 @bot.auth.set_changed
\r
352 @bot.say m.source, "the password for #{butarget.username} is now #{butarget.password}" if things.include?("password")
\r
353 return auth_manage_user(m, {:data => ["show"] + things - ["password"]})
\r
356 when :add, :rm, :remove, :del, :delete
\r
357 return m.reply "you can't change the default user" if butarget == @bot.auth.everyone and !botuser.permit?("auth::edit::default")
\r
358 return m.reply "you can't edit #{butarget.username}" if butarget != botuser and !botuser.permit?("auth::edit::other")
\r
361 if arg.nil? or arg !~ /netmasks?/ or splits[2].nil?
\r
362 return m.reply "I can only add/remove netmasks. See +help user add+ for more instructions"
\r
365 method = cmd.to_sym == :add ? :add_netmask : :delete_netmask
\r
369 splits[2..-1].each { |mask|
\r
371 butarget.send(method, mask.to_irc_netmask(:server => @bot.server))
\r
376 m.reply "I failed to #{cmd} #{failed.join(', ')}" unless failed.empty?
\r
377 @bot.auth.set_changed
\r
378 return auth_manage_user(m, {:data => ["show", "netmasks"] })
\r
381 m.reply "sorry, I don't know how to #{m.message}"
\r
385 def auth_tell_password(m, params)
\r
386 user = params[:user]
\r
387 botuser = params[:botuser]
\r
388 m.reply "I'm not telling the master password to anyway, pal" if botuser == @bot.auth.botowner
\r
389 msg = "the password for #{botuser.username} is #{botuser.password}"
\r
391 @bot.say m.source, "I told #{user} that " + msg
\r
396 auth = AuthModule.new
\r
398 auth.map "user tell :user the password for :botuser",
\r
399 :action => 'auth_tell_password',
\r
400 :auth_path => 'user::tell'
\r
402 auth.map "user *data",
\r
403 :action => 'auth_manage_user'
\r
405 auth.default_auth("user", true)
\r
406 auth.default_auth("edit::other", false)
\r
409 :action => 'auth_whoami',
\r
410 :auth_path => '!*!'
\r
412 auth.map "login :botuser :password",
\r
413 :action => 'auth_login',
\r
415 :defaults => { :password => nil },
\r
416 :auth_path => '!login!'
\r
418 auth.map "login :botuser",
\r
419 :action => 'auth_login',
\r
420 :auth_path => '!login!'
\r
423 :action => 'auth_autologin',
\r
424 :auth_path => '!login!'
\r
426 auth.map "permissions set *args for :user",
\r
427 :action => 'auth_set',
\r
428 :auth_path => ':edit::set:'
\r
430 auth.map "permissions reset *args for :user",
\r
431 :action => 'auth_reset',
\r
432 :auth_path => ':edit::reset:'
\r
434 auth.default_auth('*', false)
\r