4 # * user destroy: should work in two phases:
\r
5 # * <code>user destroy _botuser_</code> would queue _botuser_ for
\r
7 # * <code>user destroy _botuser_ _password_</code> would actually destroy
\r
8 # _botuser_ if it was queued and the _password_ is correct
\r
9 # * user destruction can be done without changing botuser.rb by getting the
\r
10 # save_array for @bot.auth, manipulating it, and reloading it with @bot.auth.load_array
\r
16 class AuthModule < CoreBotModule
\r
20 load_array(:default, true)
\r
21 debug "Initialized auth. Botusers: #{@bot.auth.save_array.inspect}"
\r
28 def save_array(key=:default)
\r
29 if @bot.auth.changed?
\r
30 @registry[key] = @bot.auth.save_array
\r
31 @bot.auth.reset_changed
\r
32 debug "saved botusers (#{key}): #{@registry[key].inspect}"
\r
36 def load_array(key=:default, forced=false)
\r
37 debug "loading botusers (#{key}): #{@registry[key].inspect}"
\r
38 @bot.auth.load_array(@registry[key], forced) if @registry.has_key?(key)
\r
41 # The permission parameters accept arguments with the following syntax:
\r
42 # cmd_path... [on #chan .... | in here | in private]
\r
43 # This auxiliary method scans the array _ar_ to see if it matches
\r
44 # the given syntax: it expects + or - signs in front of _cmd_path_
\r
45 # elements when _setting_ = true
\r
47 # It returns an array whose first element is the array of cmd_path,
\r
48 # the second element is an array of locations and third an array of
\r
49 # warnings occurred while parsing the strings
\r
51 def parse_args(ar, setting)
\r
56 next_must_be_chan = false
\r
59 ar.each_with_index { |x, i|
\r
60 if doing_cmds # parse cmd_path
\r
61 # check if the list is done
\r
62 if x == "on" or x == "in"
\r
64 next_must_be_chan = true if x == "on"
\r
67 if "+-".include?(x[0])
\r
68 warns << ArgumentError("please do not use + or - in front of command #{x} when resetting") unless setting
\r
70 warns << ArgumentError("+ or - expected in front of #{x}") if setting
\r
73 else # parse locations
\r
79 case next_must_be_chan
\r
81 locs << x.gsub(/^here$/,'_').gsub(/^private$/,'?')
\r
83 warns << ArgumentError("#{x} doesn't look like a channel name") unless @bot.server.supports[:chantypes].include?(x[0])
\r
92 warns << "trailing comma" if wants_more
\r
93 warns << "you probably forgot a comma" unless last_idx == ar.length - 1
\r
94 return cmds, locs, warns
\r
97 def auth_set(m, params)
\r
98 cmds, locs, warns = parse_args(params[:args])
\r
99 errs = warns.select { |w| w.kind_of?(Exception) }
\r
101 m.reply "couldn't satisfy your request: #{errs.join(',')}"
\r
104 user = params[:user].sub(/^all$/,"everyone")
\r
106 bu = @bot.auth.get_botuser(user)
\r
108 return m.reply "couldn't find botuser #{user}"
\r
117 ch = "?" if loc == "_"
\r
119 ch = m.target.to_s if loc == "_"
\r
121 cmds.each { |setval|
\r
122 val = setval[0].chr == '+'
\r
123 cmd = setval[1..-1]
\r
124 bu.set_permission(cmd, val, ch)
\r
128 m.reply "Something went wrong while trying to set the permissions"
\r
131 @bot.auth.set_changed
\r
132 debug "User #{user} permissions changed"
\r
133 m.reply "Ok, #{user} now also has permissions #{params[:args].join(' ')}"
\r
136 def get_botuser_for(user)
\r
137 @bot.auth.irc_to_botuser(user)
\r
140 def get_botusername_for(user)
\r
141 get_botuser_for(user).username
\r
145 "welcome, #{get_botusername_for(user)}"
\r
148 def auth_login(m, params)
\r
150 case @bot.auth.login(m.source, params[:botuser], params[:password])
\r
152 m.reply welcome(m.source)
\r
153 @bot.auth.set_changed
\r
155 m.reply "sorry, can't do"
\r
158 m.reply "couldn't login: #{e}"
\r
163 def auth_autologin(m, params)
\r
164 u = do_autologin(m.source)
\r
167 m.reply "I couldn't find anything to let you login automatically"
\r
169 m.reply welcome(m.source)
\r
173 def do_autologin(user)
\r
174 @bot.auth.autologin(user)
\r
177 def auth_whoami(m, params)
\r
180 # rep << m.source.nick << ", "
\r
183 rep << get_botusername_for(m.source).gsub(/^everyone$/, "no one that I know").gsub(/^owner$/, "my boss")
\r
187 def help(plugin, topic="")
\r
190 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
192 return "whoami: names the botuser you're linked to"
\r
193 when /^permission syntax/
\r
194 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
196 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
198 return "user show <what> : shows info about the user; <what> can be any of autologin, login-by-mask, netmasks"
\r
199 when /^user (en|dis)able/
\r
200 return "user enable|disable <what> : turns on or off <what> (autologin, login-by-mask)"
\r
202 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
203 when /^user (add|rm)/
\r
204 return "user add|rm netmask <mask> : adds/removes netmask <mask> from the list of netmasks known to the botuser you're linked to"
\r
206 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
208 return "user tell <who> the password for <botuser> : contacts <who> in private to tell him/her the password for <botuser>"
\r
209 when /^user create/
\r
210 return "user create <name> <password> : create botuser named <name> with password <password>. The password can be omitted, in which case a random one will be generated. The <name> should only contain alphanumeric characters and the underscore (_)"
\r
212 return "user list : lists all the botusers"
\r
214 return "user show, enable|disable, add|rm netmask, set, reset, tell, create, list"
\r
216 return "#{name}: login, whoami, permission syntax, permissions, user"
\r
221 "sorry, I need more arguments to #{cmd}"
\r
224 def not_args(cmd, *stuff)
\r
225 "I can only #{cmd} these: #{stuff.join(', ')}"
\r
228 def set_prop(botuser, prop, val)
\r
229 k = prop.to_s.gsub("-","_")
\r
230 botuser.send( (k + "=").to_sym, val)
\r
233 def reset_prop(botuser, prop)
\r
234 k = prop.to_s.gsub("-","_")
\r
235 botuser.send( ("reset_"+k).to_sym)
\r
238 def ask_bool_prop(botuser, prop)
\r
239 k = prop.to_s.gsub("-","_")
\r
240 botuser.send( (k + "?").to_sym)
\r
243 def auth_manage_user(m, params)
\r
244 splits = params[:data]
\r
247 return auth_whoami(m, params) if cmd.nil?
\r
249 botuser = get_botuser_for(m.source)
\r
250 # By default, we do stuff on the botuser the irc user is bound to
\r
253 has_for = splits[-2] == "for"
\r
254 butarget = @bot.auth.get_botuser(splits[-1]) if has_for
\r
255 return m.reply "you can't mess with #{butarget.username}" if butarget == @bot.auth.botowner && botuser != butarget
\r
256 splits.slice!(-2,2) if has_for
\r
258 bools = [:autologin, :"login-by-mask"]
\r
259 can_set = [:password]
\r
260 can_addrm = [:netmasks]
\r
261 can_reset = bools + can_set + can_addrm
\r
266 return "you can't see the properties of #{butarget.username}" if botuser != butarget and !botuser.permit?("auth::show::other")
\r
272 if botuser != butarget
\r
273 return m.reply "no way I'm telling you the master password!" if butarget == @bot.auth.botowner
\r
274 return m.reply "you can't ask for someone else's password"
\r
276 return m.reply "c'mon, you can't be asking me seriously to tell you the password in public!" if m.public?
\r
277 return m.reply "the password for #{butarget.username} is #{butarget.password}"
\r
279 props = splits[1..-1]
\r
286 next if k == :password
\r
290 str.last << "not" unless ask_bool_prop(butarget, k)
\r
291 str.last << " #{k}"
\r
294 if butarget.netmasks.empty?
\r
295 str.last << "no netmasks"
\r
297 str.last << butarget.netmasks.join(", ")
\r
301 return m.reply "#{butarget.username} #{str.join('; ')}"
\r
303 when :enable, :disable
\r
304 return m.reply "you can't change the default user" if butarget == @bot.auth.everyone and !botuser.permit?("auth::edit::other::default")
\r
305 return m.reply "you can't edit #{butarget.username}" if butarget != botuser and !botuser.permit?("auth::edit::other")
\r
307 return m.reply need_args(cmd) unless splits[1]
\r
310 splits[1..-1].each { |a|
\r
312 if bools.include?(arg)
\r
313 set_prop(butarget, arg, cmd.to_sym == :enable)
\r
320 m.reply "I ignored #{skipped.join(', ')} because " + not_args(cmd, *bools) unless skipped.empty?
\r
322 m.reply "I haven't changed anything"
\r
324 @bot.auth.set_changed
\r
325 return auth_manage_user(m, {:data => ["show"] + things })
\r
329 return m.reply "you can't change the default user" if butarget == @bot.auth.everyone and !botuser.permit?("auth::edit::default")
\r
330 return m.reply "you can't edit #{butarget.username}" if butarget != botuser and !botuser.permit?("auth::edit::other")
\r
332 return m.reply need_args(cmd) unless splits[1]
\r
333 arg = splits[1].to_sym
\r
334 return m.reply not_args(cmd, *can_set) unless can_set.include?(arg)
\r
336 return m.reply need_args([cmd, splits[1]].join(" ")) unless argarg
\r
337 if arg == :password && m.public?
\r
338 return m.reply "is that a joke? setting the password in public?"
\r
340 set_prop(butarget, arg, argarg)
\r
341 @bot.auth.set_changed
\r
342 auth_manage_user(m, {:data => ["show", arg] })
\r
345 return m.reply "you can't change the default user" if butarget == @bot.auth.everyone and !botuser.permit?("auth::edit::default")
\r
346 return m.reply "you can't edit #{butarget.username}" if butarget != botuser and !botuser.permit?("auth::edit::other")
\r
348 return m.reply need_args(cmd) unless splits[1]
\r
351 splits[1..-1].each { |a|
\r
353 if can_reset.include?(arg)
\r
354 reset_prop(butarget, arg)
\r
361 m.reply "I ignored #{skipped.join(', ')} because " + not_args(cmd, *can_reset) unless skipped.empty?
\r
363 m.reply "I haven't changed anything"
\r
365 @bot.auth.set_changed
\r
366 @bot.say m.source, "the password for #{butarget.username} is now #{butarget.password}" if things.include?("password")
\r
367 return auth_manage_user(m, {:data => ["show"] + things - ["password"]})
\r
370 when :add, :rm, :remove, :del, :delete
\r
371 return m.reply "you can't change the default user" if butarget == @bot.auth.everyone and !botuser.permit?("auth::edit::default")
\r
372 return m.reply "you can't edit #{butarget.username}" if butarget != botuser and !botuser.permit?("auth::edit::other")
\r
375 if arg.nil? or arg !~ /netmasks?/ or splits[2].nil?
\r
376 return m.reply "I can only add/remove netmasks. See +help user add+ for more instructions"
\r
379 method = cmd.to_sym == :add ? :add_netmask : :delete_netmask
\r
383 splits[2..-1].each { |mask|
\r
385 butarget.send(method, mask.to_irc_netmask(:server => @bot.server))
\r
390 m.reply "I failed to #{cmd} #{failed.join(', ')}" unless failed.empty?
\r
391 @bot.auth.set_changed
\r
392 return auth_manage_user(m, {:data => ["show", "netmasks"] })
\r
395 m.reply "sorry, I don't know how to #{m.message}"
\r
399 def auth_tell_password(m, params)
\r
400 user = params[:user]
\r
402 botuser = @bot.auth.get_botuser(params[:botuser])
\r
404 return m.reply "coudln't find botuser #{params[:botuser]})"
\r
406 m.reply "I'm not telling the master password to anyway, pal" if botuser == @bot.auth.botowner
\r
407 msg = "the password for botuser #{botuser.username} is #{botuser.password}"
\r
409 @bot.say m.source, "I told #{user} that " + msg
\r
412 def auth_create_user(m, params)
\r
413 name = params[:name]
\r
414 password = params[:password]
\r
415 return m.reply "are you nuts, creating a botuser with a publicly known password?" if m.public? and not password.nil?
\r
417 bu = @bot.auth.create_botuser(name, password)
\r
418 @bot.auth.set_changed
\r
420 return m.reply "Failed to create #{name}: #{e}"
\r
421 debug e.inspect + "\n" + e.backtrace.join("\n")
\r
423 m.reply "Created botuser #{bu.username}"
\r
426 def auth_list_users(m, params)
\r
427 # TODO name regexp to filter results
\r
428 list = @bot.auth.save_array.inject([]) { |list, x| list << x[:username] } - ['everyone', 'owner']
\r
429 return m.reply "I have no botusers other than the default ones" if list.empty?
\r
430 return m.reply "Botuser#{'s' if list.length > 1}: #{list.join(', ')}"
\r
435 auth = AuthModule.new
\r
437 auth.map "user tell :user the password for :botuser",
\r
438 :action => 'auth_tell_password',
\r
439 :auth_path => 'user::tell'
\r
441 auth.map "user create :name :password",
\r
442 :action => 'auth_create_user',
\r
443 :defaults => {:password => nil},
\r
444 :auth_path => 'user::create!'
\r
446 auth.map "user list",
\r
447 :action => 'auth_list_users',
\r
448 :auth_path => 'user::list!'
\r
450 auth.map "user *data",
\r
451 :action => 'auth_manage_user'
\r
453 auth.default_auth("user", true)
\r
454 auth.default_auth("edit::other", false)
\r
457 :action => 'auth_whoami',
\r
458 :auth_path => '!*!'
\r
460 auth.map "login :botuser :password",
\r
461 :action => 'auth_login',
\r
463 :defaults => { :password => nil },
\r
464 :auth_path => '!login!'
\r
466 auth.map "login :botuser",
\r
467 :action => 'auth_login',
\r
468 :auth_path => '!login!'
\r
471 :action => 'auth_autologin',
\r
472 :auth_path => '!login!'
\r
474 auth.map "permissions set *args for :user",
\r
475 :action => 'auth_set',
\r
476 :auth_path => ':edit::set:'
\r
478 auth.map "permissions reset *args for :user",
\r
479 :action => 'auth_reset',
\r
480 :auth_path => ':edit::reset:'
\r
482 auth.default_auth('*', false)
\r