]> git.netwichtig.de Git - user/henk/code/ruby/rbot.git/commitdiff
Remote Service Provider: implement auth control for remote clients. They can now...
authorGiuseppe Bilotta <giuseppe.bilotta@gmail.com>
Mon, 12 Feb 2007 21:04:48 +0000 (21:04 +0000)
committerGiuseppe Bilotta <giuseppe.bilotta@gmail.com>
Mon, 12 Feb 2007 21:04:48 +0000 (21:04 +0000)
ChangeLog
lib/rbot/core/remote.rb

index d3b3a923bff13145b7d2db8c32d7e4301b143212..5d3a72582ce2a9848c585faea94674e66facd611 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -9,6 +9,9 @@
        * Remote Service Provider: BotModules can now include the
        RemoteBotModule interface that provides them with the remote_map()
        method that works just like the map() method, but for remote commands
+       * Remote Service Provider: Remote clients can now login remotely
+       before executing commands. This in fact integrates the remote access
+       auth security with the User/BotUser used on IRC itself.
 
 2007-02-08  Giuseppe Bilotta <giuseppe.bilotta@gmail.com>
 
index 56ba502de278fe838b0036b7398aead9d7dbfef6..de0ade5f32b7ff6986d2a5edd46f8074b3088d4c 100644 (file)
@@ -9,14 +9,76 @@
 #
 # From an idea by halorgium <rbot@spork.in>.
 #
-# TODO client ID and auth
+# TODO find a way to manage session id (logging out, manually and/or
+# automatically)
 #
 
 require 'drb/drb'
 
 module ::Irc
 
-  # A RemoteCommand is similar to a BaiscUserMessage
+  module Auth
+
+    # We extend the BotUser class to handle remote logins
+    #
+    class BotUser
+
+      # A rather simple method to handle remote logins. Nothing special, just a
+      # password check.
+      #
+      def remote_login(password)
+        if password == @password
+          debug "remote login for #{self.inspect} succeeded"
+          return true
+        else
+          return false
+        end
+      end
+    end
+
+    # We extend the AuthManagerClass to handle remote logins
+    #
+    class AuthManagerClass
+
+      MAX_SESSION_ID = 2**128 - 1
+
+      # Creates a session id when the given password matches the given
+      # botusername
+      #
+      def remote_login(botusername, pwd)
+        @remote_users = Hash.new unless defined? @remote_users
+        n = BotUser.sanitize_username(botusername)
+        k = n.to_sym
+        raise "No such BotUser #{n}" unless include?(k)
+        bu = @allbotusers[k]
+        if bu.remote_login(pwd)
+          raise "ran out of session ids!" if @remote_users.length == MAX_SESSION_ID
+          session_id = rand(MAX_SESSION_ID)
+          while @remote_users.has_key?(session_id)
+            session_id = rand(MAX_SESSION_ID)
+          end
+          @remote_users[session_id] = bu
+          return session_id
+        end
+        return false
+      end
+
+      # Returns the botuser associated with the given session id
+      def remote_user(session_id)
+        return everyone unless session_id
+        return nil unless defined? @remote_users
+        if @remote_users.has_key?(session_id)
+          return @remote_users[session_id]
+        else
+          return nil
+        end
+      end
+    end
+
+  end
+
+
+  # A RemoteMessage is similar to a BasicUserMessage
   #
   class RemoteMessage
     # associated bot
@@ -49,8 +111,13 @@ module ::Irc
     end
   end
 
+  # The RemoteDispatcher is a kind of MessageMapper, tuned to handle
+  # RemoteMessages
+  #
   class RemoteDispatcher < MessageMapper
 
+    # It is initialized by passing it the bot instance
+    #
     def initialize(bot)
       super
     end
@@ -63,8 +130,8 @@ module ::Irc
       return @templates.length - 1
     end
 
-    # The map method for the RemoteDispatcher returns the index of the inserted
-    # template
+    # The unmap method for the RemoteDispatcher nils the template at the given index,
+    # therefore effectively removing the mapping
     #
     def unmap(botmodule, handle)
       tmpl = @templates[handle]
@@ -75,12 +142,19 @@ module ::Irc
     end
 
     # We redefine the handle() method from MessageMapper, taking into account
-    # that @parent is a bot, and that we don't handle fallbacks
+    # that @parent is a bot, and that we don't handle fallbacks.
+    #
+    # On failure to dispatch anything, the method returns false. If dispatching
+    # is successfull, the method returns a Hash.
     #
+    # Presently, the hash returned on success has only one key, :return, whose
+    # value is the actual return value of the successfull dispatch.
+    # 
     # TODO this same kind of mechanism could actually be used in MessageMapper
     # itself to be able to handle the case of multiple plugins having the same
     # 'first word' ...
     #
+    #
     def handle(m)
       return false if @templates.empty?
       failures = []
@@ -97,18 +171,17 @@ module ::Irc
             failures << [tmpl, "#{botmodule} does not respond to action #{action}"]
             next
           end
-          # TODO
-          # auth = tmpl.options[:full_auth_path]
-          # debug "checking auth for #{auth}"
-          # if m.bot.auth.allow?(auth, m.source, m.replyto)
+          auth = tmpl.options[:full_auth_path]
+          debug "checking auth for #{auth}"
+          # We check for private permission
+          if m.bot.auth.allow?(auth, m.source, '?')
             debug "template match found and auth'd: #{action.inspect} #{options.inspect}"
-            botmodule.send(action, m, options)
-            return true
-          # end
-          # debug "auth failed for #{auth}"
-          # # if it's just an auth failure but otherwise the match is good,
-          # # don't try any more handlers
-          # return false
+            return :return => botmodule.send(action, m, options)
+          end
+          debug "auth failed for #{auth}"
+          # if it's just an auth failure but otherwise the match is good,
+          # don't try any more handlers
+          return false
         end
       end
       failures.each {|f, r|
@@ -125,6 +198,16 @@ module ::Irc
     # The Irc::IrcBot::RemoteObject class represents and object that will take care
     # of interfacing with remote clients
     #
+    # Example client session:
+    #
+    #   require 'drb'
+    #   rbot = DRbObject.new_with_uri('druby://localhost:7268')
+    #   id = rbot.delegate(nil, 'remote login someuser somepass')[:return]
+    #   rbot.delegate(id, 'some secret command')
+    #
+    # Of course, the remote login is only neede for commands which may not be available
+    # to everyone
+    #
     class RemoteObject
 
       # We don't want this object to be copied clientside, so we make it undumpable
@@ -137,17 +220,18 @@ module ::Irc
 
       # The delegate method. This is the main method used by remote clients to send
       # commands to the bot. Most of the time, the method will be called with only
-      # two parameters (authorization code and a String), but we allow more parameters
-      # for future expansions
+      # two parameters (session id and a String), but we allow more parameters
+      # for future expansions.
       #
-      def delegate(auth, *pars)
+      # The session_id can be nil, meaning that the remote client wants to work as
+      # an anoynomus botuser.
+      #
+      def delegate(session_id, *pars)
         warn "Ignoring extra parameters" if pars.length > 1
         cmd = pars.first
-        # TODO implement auth <-> client conversion
-        # We need at least a RemoteBotUser class derived from Irc::Auth::BotUser
-        # and a way to associate the auth info to the appropriate RemoteBotUser class
-        client = auth
-        debug "Trying to dispatch command #{cmd.inspect} authorized by #{auth.inspect}"
+        client = @bot.auth.remote_user(session_id)
+        raise "No such session id #{session_id}" unless client
+        debug "Trying to dispatch command #{cmd.inspect} from #{client.inspect} authorized by #{session_id.inspect}"
         m = RemoteMessage.new(@bot, client, cmd)
         @bot.remote_dispatcher.handle(m)
       end
@@ -291,6 +375,12 @@ class RemoteModule < CoreBotModule
     @bot.say params[:channel], "This is a remote test"
   end
 
+  def remote_login(m, params)
+    id = @bot.auth.remote_login(params[:botuser], params[:password])
+    raise "login failed" unless id
+    return id
+  end
+
 end
 
 remote = RemoteModule.new
@@ -303,7 +393,12 @@ remote.map "remote stop",
   :action => 'handle_stop',
   :auth_path => ':manage:'
 
+remote.default_auth('*', false)
+
 remote.remote_map "remote test :channel",
   :action => 'remote_test'
 
-remote.default_auth('*', false)
+remote.remote_map "remote login :botuser :password",
+  :action => 'remote_login'
+
+remote.default_auth('login', true)