]> git.netwichtig.de Git - user/henk/code/ruby/rbot.git/blobdiff - lib/rbot/core/auth.rb
Ruby 1.9 can intern empty strings
[user/henk/code/ruby/rbot.git] / lib / rbot / core / auth.rb
index 0b8c8da4c2c3a9b2efa7a6643640e27c306d2094..ce21ccc91c8633996345d32357fb48f59b0a4e65 100644 (file)
@@ -4,8 +4,6 @@
 # :title: rbot auth management from IRC
 #
 # Author:: Giuseppe "Oblomov" Bilotta <giuseppe.bilotta@gmail.com>
-# Copyright:: (C) 2006,2007 Giuseppe Bilotta
-# License:: GPL v2
 
 class AuthModule < CoreBotModule
 
@@ -39,6 +37,13 @@ class AuthModule < CoreBotModule
   def load_array(key=:default, forced=false)
     debug "loading botusers (#{key}): #{@registry[key].pretty_inspect}"
     @bot.auth.load_array(@registry[key], forced) if @registry.has_key?(key)
+    if @bot.auth.botowner.password != @bot.config['auth.password']
+      error "Master password is out of sync!"
+      debug "  db password: #{@bot.auth.botowner.password}"
+      debug "conf password: #{@bot.config['auth.password']}"
+      error "Using conf password"
+      @bot.auth.botowner.password = @bot.config['auth.password']
+    end
   end
 
   # The permission parameters accept arguments with the following syntax:
@@ -155,7 +160,7 @@ class AuthModule < CoreBotModule
   def auth_view_perm(m, params)
     begin
       if params[:user].nil?
-        user = get_botusername_for(m.source)
+        user = get_botuser_for(m.source)
         return m.reply(_("you are owner, you can do anything")) if user.owner?
       else
         user = @bot.auth.get_botuser(params[:user].sub(/^all$/,"everyone"))
@@ -170,11 +175,11 @@ class AuthModule < CoreBotModule
       next if val.perm.empty?
       case k
       when :*
-        str << _("on any channel: ")
+        str << _("on any channel: ").dup
       when :"?"
-        str << _("in private: ")
+        str << _("in private: ").dup
       else
-        str << _("on #{k}: ")
+        str << _("on #{k}: ").dup
       end
       sub = []
       val.perm.each { |cmd, bool|
@@ -209,6 +214,86 @@ class AuthModule < CoreBotModule
     }
   end
 
+  def find_auth(pseudo)
+    k = pseudo.plugin.intern
+    cmds = @bot.plugins.commands
+    auth = nil
+    if cmds.has_key?(k)
+      cmds[k][:botmodule].handler.each do |tmpl|
+        options = tmpl.recognize(pseudo)
+        next if options.kind_of? MessageMapper::Failure
+        auth = tmpl.options[:full_auth_path]
+        break
+      end
+    end
+    return auth
+  end
+
+  def auth_allow_deny(m, p)
+    begin
+      botuser = @bot.auth.get_botuser(p[:user].sub(/^all$/,"everyone"))
+    rescue
+      return m.reply(_("couldn't find botuser %{name}") % {:name => p[:user]})
+    end
+
+    if p[:where].to_s.empty?
+      where = :*
+    else
+      where = m.parse_channel_list(p[:where].to_s).first # should only be one anyway
+    end
+
+    if p.has_key? :auth_path
+      auth_path = p[:auth_path]
+    else
+      # pseudo-message to find the template. The source is ignored, and the
+      # target is set according to where the template should be checked
+      # (public or private)
+      # This might still fail in the case of 'everywhere' for commands there are
+      # really only private
+      case where
+      when :"?"
+        pseudo_target = @bot.myself
+      when :*
+        pseudo_target = m.channel
+      else
+        pseudo_target = m.server.channel(where)
+      end
+
+      pseudo = PrivMessage.new(bot, m.server, m.source, pseudo_target, p[:stuff].to_s)
+
+      auth_path = find_auth(pseudo)
+    end
+    debug auth_path
+
+    if auth_path
+      allow = p[:allow]
+      if @bot.auth.permit?(botuser, auth_path, where)
+        return m.reply(_("%{user} can already do that") % {:user => botuser}) if allow
+      else
+        return m.reply(_("%{user} can't do that already") % {:user => botuser}) if !allow
+      end
+      cmd = PrivMessage.new(bot, m.server, m.source, m.target, "permissions set %{sign}%{path} %{where} for %{user}" % {
+        :path => auth_path,
+        :user => p[:user],
+        :sign => (allow ? '+' : '-'),
+        :where => p[:where].to_s
+      })
+      handle(cmd)
+    else
+      m.reply(_("sorry, %{cmd} doesn't look like a valid command. maybe you misspelled it, or you need to specify it should be in private?") % {
+        :cmd => p[:stuff].to_s
+      })
+    end
+  end
+
+  def auth_allow(m, p)
+    auth_allow_deny(m, p.merge(:allow => true))
+  end
+
+  def auth_deny(m, p)
+    auth_allow_deny(m, p.merge(:allow => false))
+  end
+
   def get_botuser_for(user)
     @bot.auth.irc_to_botuser(user)
   end
@@ -217,8 +302,8 @@ class AuthModule < CoreBotModule
     get_botuser_for(user).username
   end
 
-  def welcome(user)
-    _("welcome, %{user}") % {:user => get_botusername_for(user)}
+  def say_welcome(m)
+    m.reply _("welcome, %{user}") % {:user => get_botusername_for(m.source)}
   end
 
   def auth_auth(m, params)
@@ -230,7 +315,7 @@ class AuthModule < CoreBotModule
     begin
       case @bot.auth.login(m.source, params[:botuser], params[:password])
       when true
-        m.reply welcome(m.source)
+        say_welcome(m)
         @bot.auth.set_changed
       else
         m.reply _("sorry, can't do")
@@ -246,7 +331,7 @@ class AuthModule < CoreBotModule
     if u.default?
       m.reply _("I couldn't find anything to let you login automatically")
     else
-      m.reply welcome(m.source)
+      say_welcome(m)
     end
   end
 
@@ -316,17 +401,37 @@ class AuthModule < CoreBotModule
         return _("user list : lists all the botusers")
       when "destroy"
         return _("user destroy <botuser> : destroys <botuser>. This function %{highlight}must%{highlight} be called in two steps. On the first call <botuser> is queued for destruction. On the second call, which must be in the form 'user confirm destroy <botuser>', the botuser will be destroyed. If you want to cancel the destruction, issue the command 'user cancel destroy <botuser>'") % {:highlight => Bold}
+      when "export"
+        return _("user export [to <filename>]: exports user data to file <filename> (default: new-auth.users)")
+      when "import"
+        return _("user import [from <filename>]: import user data from file <filename> (default: new-auth.users)")
       else
-        return _("user topics: show, enable|disable, add|rm netmask, set, reset, tell, create, list, destroy")
+        return _("user topics: show, enable|disable, add|rm netmask, set, reset, tell, create, list, destroy, import, export")
       end
     when "auth"
-      return _("auth <masterpassword>: log in as the bot owner; other commands: login, whoami, permission syntax, permissions [re]set, permissions view, user, meet, hello")
+      return _("auth <masterpassword>: log in as the bot owner; other commands: login, whoami, permissions syntax, permissions [re]set, permissions view, user, meet, hello, allow, deny")
     when "meet"
       return _("meet <nick> [as <user>]: creates a bot user for nick, calling it user (defaults to the nick itself)")
     when "hello"
       return _("hello: creates a bot user for the person issuing the command")
+    when "allow"
+      return [
+        _("allow <user> to do <sample command> [<where>]: gives botuser <user> the permissions to execute a command such as the provided sample command"),
+        _("(in private or in channel, according to the optional <where>)."),
+        _("<sample command> should be a full command, not just the command keyword --"),
+        _("correct: allow user to do addquote stuff --"),
+        _("wrong: allow user to do addquote.")
+      ].join(" ")
+    when "deny"
+      return [
+        _("deny <user> from doing <sample command> [<where>]: removes from botuser <user> the permissions to execute a command such as the provided sample command"),
+        _("(in private or in channel, according to the optional <where>)."),
+        _("<sample command> should be a full command, not just the command keyword --"),
+        _("correct: deny user from doing addquote stuff --"),
+        _("wrong: deny user from doing addquote.")
+      ].join(" ")
     else
-      return _("auth commands: auth, login, whoami, who, permission[s], user, meet, hello")
+      return _("auth commands: auth, login, whoami, who, permission[s], user, meet, hello, allow, deny")
     end
   end
 
@@ -544,7 +649,7 @@ class AuthModule < CoreBotModule
     if !nick
       # we are actually responding to a 'hello' command
       unless m.botuser.transient?
-        m.reply @bot.lang.get('hello_X') % m.botuser
+        m.reply @bot.lang.get('hello_X') % m.botuser, :nick => false
         return
       end
       nick = m.sourcenick
@@ -563,7 +668,7 @@ class AuthModule < CoreBotModule
       met = @bot.auth.make_permanent(irc_user, buname)
       @bot.auth.set_changed
       call_event(:botuser,:post_perm, {:irc_user => irc_user, :bot_user => buname})
-      m.reply @bot.lang.get('hello_X') % met
+      m.reply @bot.lang.get('hello_X') % met, :nick => false
       @bot.say nick, _("you are now registered as %{buname}. I created a random password for you : %{pass} and you can change it at any time by telling me 'user set password <password>' in private" % {
         :buname => buname,
         :pass => met.password
@@ -608,7 +713,7 @@ class AuthModule < CoreBotModule
 
   def auth_list_users(m, params)
     # TODO name regexp to filter results
-    list = @bot.auth.save_array.inject([]) { |list, x| ['everyone', 'owner'].include?(x[:username]) ? list : list << x[:username] }
+    list = @bot.auth.save_array.inject([]) { |lst, x| ['everyone', 'owner'].include?(x[:username]) ? lst : lst << x[:username] }
     if defined?(@destroy_q)
       list.map! { |x|
         @destroy_q.include?(x) ? x + _(" (queued for destruction)") : x
@@ -624,7 +729,7 @@ class AuthModule < CoreBotModule
     buname = params[:name]
     return m.reply(_("You can't destroy %{user}") % {:user => buname}) if
            ["everyone", "owner"].include?(buname)
-    mod = params[:modifier].to_sym rescue nil
+    mod = params[:modifier].nil_or_empty? ? nil : params[:modifier].to_sym
 
     buser_array = @bot.auth.save_array
     buser_hash = buser_array.inject({}) { |h, u|
@@ -716,13 +821,13 @@ class AuthModule < CoreBotModule
 
   def auth_export(m, params)
 
-    exportfile = "#{@bot.botclass}/new-auth.users"
+    exportfile = @bot.path "new-auth.users"
 
     what = params[:things]
 
     has_to = what[-2] == "to"
     if has_to
-      exportfile = "#{@bot.botclass}/#{what[-1]}"
+      exportfile = @bot.path what[-1]
       what.slice!(-2,2)
     end
 
@@ -788,13 +893,13 @@ class AuthModule < CoreBotModule
 
   def auth_import(m, params)
 
-    importfile = "#{@bot.botclass}/new-auth.users"
+    importfile = @bot.path "new-auth.users"
 
     what = params[:things]
 
     has_from = what[-2] == "from"
     if has_from
-      importfile = "#{@bot.botclass}/#{what[-1]}"
+      importfile = @bot.path what[-1]
       what.slice!(-2,2)
     end
 
@@ -918,6 +1023,16 @@ auth.map "user list",
 auth.map "user *data",
   :action => 'auth_manage_user'
 
+auth.map "allow :user to do *stuff [*where]",
+  :action => 'auth_allow',
+  :requirements => {:where => /^(?:anywhere|everywhere|[io]n \S+)$/},
+  :auth_path => ':edit::other:'
+
+auth.map "deny :user from doing *stuff [*where]",
+  :action => 'auth_deny',
+  :requirements => {:where => /^(?:anywhere|everywhere|[io]n \S+)$/},
+  :auth_path => ':edit::other:'
+
 auth.default_auth("user", true)
 auth.default_auth("edit::other", false)