#-- vim:sw=2:et\r
#++\r
-\r
+#\r
+# :title: rbot auth management from IRC\r
+#\r
+# Author:: Giuseppe "Oblomov" Bilotta <giuseppe.bilotta@gmail.com>\r
+# Copyright:: (C) 2006,2007 Giuseppe Bilotta\r
+# License:: GPL v2\r
\r
class AuthModule < CoreBotModule\r
\r
next\r
end\r
if "+-".include?(x[0])\r
- warns << ArgumentError("please do not use + or - in front of command #{x} when resetting") unless setting\r
+ warns << ArgumentError.new("please do not use + or - in front of command #{x} when resetting") unless setting\r
else\r
- warns << ArgumentError("+ or - expected in front of #{x}") if setting\r
+ warns << ArgumentError.new("+ or - expected in front of #{x}") if setting\r
end\r
cmds << x\r
else # parse locations\r
warns << ArgumentError("#{x} doesn't look like a channel name") unless @bot.server.supports[:chantypes].include?(x[0])\r
locs << x\r
end\r
- unless wants_more\r
+ unless want_more\r
last_idx = i\r
break\r
end\r
end\r
}\r
- warns << "trailing comma" if wants_more\r
+ warns << "trailing comma" if want_more\r
warns << "you probably forgot a comma" unless last_idx == ar.length - 1\r
return cmds, locs, warns\r
end\r
\r
- def auth_set(m, params)\r
- cmds, locs, warns = parse_args(params[:args])\r
+ def auth_edit_perm(m, params)\r
+\r
+ setting = m.message.split[1] == "set"\r
+ splits = params[:args]\r
+\r
+ has_for = splits[-2] == "for"\r
+ return usage(m) unless has_for\r
+\r
+ begin\r
+ user = @bot.auth.get_botuser(splits[-1].sub(/^all$/,"everyone"))\r
+ rescue\r
+ return m.reply("couldn't find botuser #{splits[-1]}")\r
+ end\r
+ return m.reply("you can't change permissions for #{user.username}") if user == @bot.auth.botowner\r
+ splits.slice!(-2,2) if has_for\r
+\r
+ cmds, locs, warns = parse_args(splits, setting)\r
errs = warns.select { |w| w.kind_of?(Exception) }\r
+\r
unless errs.empty?\r
m.reply "couldn't satisfy your request: #{errs.join(',')}"\r
return\r
end\r
- user = params[:user].sub(/^all$/,"everyone")\r
- begin\r
- bu = @bot.auth.get_botuser(user)\r
- rescue\r
- return m.reply("couldn't find botuser #{user}")\r
- end\r
+\r
if locs.empty?\r
locs << "*"\r
end\r
ch = m.target.to_s if loc == "_"\r
end\r
cmds.each { |setval|\r
- val = setval[0].chr == '+'\r
- cmd = setval[1..-1]\r
- bu.set_permission(cmd, val, ch)\r
+ if setting\r
+ val = setval[0].chr == '+'\r
+ cmd = setval[1..-1]\r
+ user.set_permission(cmd, val, ch)\r
+ else\r
+ cmd = setval\r
+ user.reset_permission(cmd, ch)\r
+ end\r
}\r
}\r
rescue => e\r
end\r
@bot.auth.set_changed\r
debug "user #{user} permissions changed"\r
- m.reply "ok, #{user} now also has permissions #{params[:args].join(' ')}"\r
+ m.okay\r
+ end\r
+\r
+ def auth_view_perm(m, params)\r
+ begin\r
+ if params[:user].nil?\r
+ user = get_botusername_for(m.source)\r
+ return m.reply("you are owner, you can do anything") if user == @bot.auth.botwoner\r
+ else\r
+ user = @bot.auth.get_botuser(params[:user].sub(/^all$/,"everyone"))\r
+ return m.reply("owner can do anything") if user.username == "owner" \r
+ end\r
+ rescue\r
+ return m.reply("couldn't find botuser #{params[:user]}")\r
+ end\r
+ perm = user.perm\r
+ str = []\r
+ perm.each { |k, val|\r
+ next if val.perm.empty?\r
+ case k\r
+ when :*\r
+ str << "on any channel: "\r
+ when :"?"\r
+ str << "in private: "\r
+ else\r
+ str << "on #{k}: "\r
+ end\r
+ sub = []\r
+ val.perm.each { |cmd, bool|\r
+ sub << (bool ? "+" : "-")\r
+ sub.last << cmd.to_s\r
+ }\r
+ str.last << sub.join(', ')\r
+ }\r
+ if str.empty?\r
+ m.reply "no permissions set for #{user.username}"\r
+ else\r
+ m.reply "permissions for #{user.username}:: #{str.join('; ')}"\r
+ end\r
end\r
\r
def get_botuser_for(user)\r
"welcome, #{get_botusername_for(user)}"\r
end\r
\r
+ def auth_auth(m, params)\r
+ params[:botuser] = 'owner'\r
+ auth_login(m,params)\r
+ end\r
+\r
def auth_login(m, params)\r
begin\r
case @bot.auth.login(m.source, params[:botuser], params[:password])\r
m.reply rep\r
end\r
\r
- def help(plugin, topic="")\r
- case topic\r
- when /^login/\r
+ def help(cmd, topic="")\r
+ case cmd\r
+ when "login"\r
return "login [<botuser>] [<pass>]: logs in to the bot as botuser <botuser> with password <pass>. When using the full form, you must contact the bot in private. <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
- when /^whoami/\r
+ when "whoami"\r
return "whoami: names the botuser you're linked to"\r
- when /^permission syntax/\r
- 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
when /^permission/\r
- 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
- when /^user show/\r
- return "user show <what> : shows info about the user; <what> can be any of autologin, login-by-mask, netmasks"\r
- when /^user (en|dis)able/\r
- return "user enable|disable <what> : turns on or off <what> (autologin, login-by-mask)"\r
- when /^user set/\r
- 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
- when /^user (add|rm)/\r
- return "user add|rm netmask <mask> : adds/removes netmask <mask> from the list of netmasks known to the botuser you're linked to"\r
- when /^user reset/\r
- 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
- when /^user tell/\r
- return "user tell <who> the password for <botuser> : contacts <who> in private to tell him/her the password for <botuser>"\r
- when /^user create/\r
- 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
- when /^user list/\r
- return "user list : lists all the botusers"\r
- when /^user destroy/\r
- return "user destroy <botuser> <password> : destroys <botuser>; this function #{Bold}must#{Bold} be called in two steps. On the first call, no password must be specified: <botuser> is then queued for destruction. On the second call, you must specify the correct password for <botuser>, and it will be destroyed. If you want to cancel the destruction, issue the command +user cancel destroy <botuser>+"\r
- when /^user/\r
- return "user show, enable|disable, add|rm netmask, set, reset, tell, create, list, destroy"\r
+ case topic\r
+ when "syntax"\r
+ 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
+ when "set", "reset", "[re]set", "(re)set"\r
+ 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
+ when "view"\r
+ return "permissions view [for <user>]: display the permissions for user <user>"\r
+ else\r
+ return "topics: syntax, (re)set, view"\r
+ end\r
+ when "user"\r
+ case topic\r
+ when "show"\r
+ return "user show <what> : shows info about the user; <what> can be any of autologin, login-by-mask, netmasks"\r
+ when /^(en|dis)able/\r
+ return "user enable|disable <what> : turns on or off <what> (autologin, login-by-mask)"\r
+ when "set"\r
+ 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
+ when "add", "rm"\r
+ return "user add|rm netmask <mask> : adds/removes netmask <mask> from the list of netmasks known to the botuser you're linked to"\r
+ when "reset"\r
+ 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
+ when "tell"\r
+ return "user tell <who> the password for <botuser> : contacts <who> in private to tell him/her the password for <botuser>"\r
+ when "create"\r
+ 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
+ when "list"\r
+ return "user list : lists all the botusers"\r
+ when "destroy"\r
+ return "user destroy <botuser> <password> : destroys <botuser>; this function #{Bold}must#{Bold} be called in two steps. On the first call, no password must be specified: <botuser> is then queued for destruction. On the second call, you must specify the correct password for <botuser>, and it will be destroyed. If you want to cancel the destruction, issue the command +user cancel destroy <botuser>+"\r
+ else\r
+ return "user show, enable|disable, add|rm netmask, set, reset, tell, create, list, destroy"\r
+ end\r
else\r
- return "#{name}: login, whoami, permission syntax, permissions, user"\r
+ return "#{name}: login, whoami, permission syntax, permissions [re]set, permissions view, user"\r
end\r
end\r
\r
can_set = [:password]\r
can_addrm = [:netmasks]\r
can_reset = bools + can_set + can_addrm\r
+ can_show = can_reset + ["perms"]\r
\r
case cmd.to_sym\r
\r
copying = m.message.split[1] == "copy"\r
begin\r
if copying\r
- h = buser_hash[source].dup \r
+ h = {}\r
+ buser_hash[source].each { |k, val|\r
+ h[k] = val.dup\r
+ }\r
else\r
h = buser_hash[source]\r
end\r
\r
end\r
\r
+ def auth_export(m, params)\r
+\r
+ exportfile = "#{@bot.botclass}/new-auth.users"\r
+\r
+ what = params[:things]\r
+\r
+ has_to = what[-2] == "to"\r
+ if has_to\r
+ exportfile = "#{@bot.botclass}/#{what[-1]}"\r
+ what.slice!(-2,2)\r
+ end\r
+\r
+ what.delete("all")\r
+\r
+ m.reply "selecting data to export ..."\r
+\r
+ buser_array = @bot.auth.save_array\r
+ buser_hash = buser_array.inject({}) { |h, u|\r
+ h[u[:username]] = u\r
+ h\r
+ }\r
+\r
+ if what.empty?\r
+ we_want = buser_hash\r
+ else\r
+ we_want = buser_hash.delete_if { |key, val|\r
+ not what.include?(key)\r
+ }\r
+ end\r
+\r
+ m.reply "preparing data for export ..."\r
+ begin\r
+ yaml_hash = {}\r
+ we_want.each { |k, val|\r
+ yaml_hash[k] = {}\r
+ val.each { |kk, v|\r
+ case kk\r
+ when :username\r
+ next\r
+ when :netmasks\r
+ yaml_hash[k][kk] = []\r
+ v.each { |nm|\r
+ yaml_hash[k][kk] << {\r
+ :fullform => nm.fullform,\r
+ :casemap => nm.casemap.to_s\r
+ }\r
+ }\r
+ else\r
+ yaml_hash[k][kk] = v\r
+ end\r
+ }\r
+ }\r
+ rescue => e\r
+ m.reply "failed to prepare data: #{e}"\r
+ debug e.backtrace.dup.unshift(e.inspect).join("\n")\r
+ return\r
+ end\r
+\r
+ m.reply "exporting to #{exportfile} ..."\r
+ begin\r
+ # m.reply yaml_hash.inspect\r
+ File.open(exportfile, "w") do |file|\r
+ file.puts YAML::dump(yaml_hash)\r
+ end\r
+ rescue => e\r
+ m.reply "failed to export users: #{e}"\r
+ debug e.backtrace.dup.unshift(e.inspect).join("\n")\r
+ return\r
+ end\r
+ m.reply "done"\r
+ end\r
+\r
+ def auth_import(m, params)\r
+\r
+ importfile = "#{@bot.botclass}/new-auth.users"\r
+\r
+ what = params[:things]\r
+\r
+ has_from = what[-2] == "from"\r
+ if has_from\r
+ importfile = "#{@bot.botclass}/#{what[-1]}"\r
+ what.slice!(-2,2)\r
+ end\r
+\r
+ what.delete("all")\r
+\r
+ m.reply "reading #{importfile} ..."\r
+ begin\r
+ yaml_hash = YAML::load_file(importfile)\r
+ rescue => e\r
+ m.reply "failed to import from: #{e}"\r
+ debug e.backtrace.dup.unshift(e.inspect).join("\n")\r
+ return\r
+ end\r
+\r
+ # m.reply yaml_hash.inspect\r
+\r
+ m.reply "selecting data to import ..."\r
+\r
+ if what.empty?\r
+ we_want = yaml_hash\r
+ else\r
+ we_want = yaml_hash.delete_if { |key, val|\r
+ not what.include?(key)\r
+ }\r
+ end\r
+\r
+ m.reply "parsing data from import ..."\r
+\r
+ buser_hash = {}\r
+\r
+ begin\r
+ yaml_hash.each { |k, val|\r
+ buser_hash[k] = { :username => k }\r
+ val.each { |kk, v|\r
+ case kk\r
+ when :netmasks\r
+ buser_hash[k][kk] = []\r
+ v.each { |nm|\r
+ buser_hash[k][kk] << nm[:fullform].to_irc_netmask(:casemap => nm[:casemap].to_irc_casemap).to_irc_netmask(:server => @bot.server)\r
+ }\r
+ else\r
+ buser_hash[k][kk] = v\r
+ end\r
+ }\r
+ }\r
+ rescue => e\r
+ m.reply "failed to parse data: #{e}"\r
+ debug e.backtrace.dup.unshift(e.inspect).join("\n")\r
+ return\r
+ end\r
+\r
+ # m.reply buser_hash.inspect\r
+\r
+ org_buser_array = @bot.auth.save_array\r
+ org_buser_hash = org_buser_array.inject({}) { |h, u|\r
+ h[u[:username]] = u\r
+ h\r
+ }\r
+\r
+ # TODO we may want to do a(n optional) key-by-key merge\r
+ #\r
+ org_buser_hash.merge!(buser_hash)\r
+ new_buser_array = org_buser_hash.values\r
+ @bot.auth.load_array(new_buser_array, true)\r
+ @bot.auth.set_changed\r
+\r
+ m.reply "done"\r
+ end\r
+\r
end\r
\r
auth = AuthModule.new\r
\r
+auth.map "user export *things",\r
+ :action => 'auth_export',\r
+ :defaults => { :things => ['all'] },\r
+ :auth_path => ':manage:fedex:'\r
+\r
+auth.map "user import *things",\r
+ :action => 'auth_import',\r
+ :auth_path => ':manage:fedex:'\r
+\r
auth.map "user create :name :password",\r
:action => 'auth_create_user',\r
:defaults => {:password => nil},\r
- :auth_path => 'user::manage::create!'\r
+ :auth_path => ':manage:'\r
\r
-auth.map "user cancel destroy :name :password",\r
+auth.map "user [cancel] destroy :name :password",\r
:action => 'auth_destroy_user',\r
:defaults => { :password => nil },\r
- :auth_path => 'user::manage::destroy::cancel!'\r
+ :auth_path => ':manage::destroy:'\r
\r
-auth.map "user destroy :name :password",\r
- :action => 'auth_destroy_user',\r
- :defaults => { :password => nil },\r
- :auth_path => 'user::manage::destroy!'\r
-\r
-auth.map "user copy :source :dest",\r
+auth.map "user copy :source [to] :dest",\r
:action => 'auth_copy_ren_user',\r
- :auth_path => 'user::manage::copy!'\r
+ :auth_path => ':manage:'\r
\r
-auth.map "user rename :source :dest",\r
+auth.map "user rename :source [to] :dest",\r
:action => 'auth_copy_ren_user',\r
- :auth_path => 'user::manage::rename!'\r
+ :auth_path => ':manage:'\r
\r
auth.default_auth("user::manage", false)\r
\r
auth.map "user tell :user the password for :botuser",\r
:action => 'auth_tell_password',\r
- :auth_path => 'user::tell'\r
+ :auth_path => '::'\r
\r
auth.map "user list",\r
:action => 'auth_list_users',\r
- :auth_path => 'user::list!'\r
+ :auth_path => '::'\r
\r
auth.map "user *data",\r
:action => 'auth_manage_user'\r
:action => 'auth_whoami',\r
:auth_path => '!*!'\r
\r
+auth.map "auth :password",\r
+ :action => 'auth_auth',\r
+ :public => false,\r
+ :auth_path => '!login!'\r
+\r
auth.map "login :botuser :password",\r
:action => 'auth_login',\r
:public => false,\r
:action => 'auth_autologin',\r
:auth_path => '!login!'\r
\r
-auth.map "permissions set *args for :user",\r
- :action => 'auth_set',\r
+auth.map "permissions set *args",\r
+ :action => 'auth_edit_perm',\r
:auth_path => ':edit::set:'\r
\r
-auth.map "permissions reset *args for :user",\r
- :action => 'auth_reset',\r
+auth.map "permissions reset *args",\r
+ :action => 'auth_edit_perm',\r
:auth_path => ':edit::reset:'\r
\r
+auth.map "permissions view [for :user]",\r
+ :action => 'auth_view_perm',\r
+ :auth_path => '::'\r
+\r
auth.default_auth('*', false)\r
\r