From c9b76ddbc6ada354e0c1f14a14fcd1cdd7c1230c Mon Sep 17 00:00:00 2001 From: Giuseppe Bilotta Date: Wed, 2 Aug 2006 16:26:29 +0000 Subject: Auth now follows the specs defined in NewAuthModule even though there is no actual auth coremodule. config.rb needs to be split into a class definition file and a coremodule that manages it --- lib/rbot/botuser.rb | 86 +++++++++++++++++++++++++++++++++++++---------- lib/rbot/config.rb | 33 +++++++++--------- lib/rbot/core/core.rb | 61 ++++++++++++++++----------------- lib/rbot/ircbot.rb | 1 + lib/rbot/messagemapper.rb | 46 ++++++++++++++++++++++--- lib/rbot/plugins.rb | 71 ++++++++++++++++++++++++-------------- 6 files changed, 206 insertions(+), 92 deletions(-) (limited to 'lib') diff --git a/lib/rbot/botuser.rb b/lib/rbot/botuser.rb index 98408a0d..6c84a93b 100644 --- a/lib/rbot/botuser.rb +++ b/lib/rbot/botuser.rb @@ -252,13 +252,29 @@ module Irc @perm = {} end + # Inspection simply inspects the internal hash + def inspect + @perm.inspect + end + # Sets the permission for command _cmd_ to _val_, - # creating intermediate permissions if needed. # def set_permission(cmd, val) - raise TypeError, "#{val.inspect} must be true or false" unless [true,false].include?(val) Irc::error_if_not_command(cmd) - @perm[cmd.command] = val + case val + when true, false + @perm[cmd.command] = val + when nil + @perm.delete(cmd.command) + else + raise TypeError, "#{val.inspect} must be true or false" unless [true,false].include?(val) + end + end + + # Resets the permission for command _cmd_ + # + def reset_permission(cmd) + set_permission(cmd, nil) end # Tells if command _cmd_ is permitted. We do this by returning @@ -313,6 +329,12 @@ module Irc end end + # Resets the permission for command _cmd_ on channel _chan_ + # + def reset_permission(cmd, chan ="*") + set_permission(cmd, nil, chan) + end + # Checks if BotUser is allowed to do something on channel _chan_, # or on all channels if _chan_ is nil # @@ -415,17 +437,27 @@ module Irc end - # This is the anonymous BotUser: it's used for all users which haven't + # This is the default BotUser: it's used for all users which haven't # identified with the bot # - class AnonBotUserClass < BotUser + class DefaultBotUserClass < BotUser include Singleton def initialize - super("anonymous") + super("everyone") + @default_perm = PermissionSet.new end private :login, :add_netmask, :delete_netmask - # Anon knows everybody + # Sets the default permission for the default user (i.e. the ones + # set by the BotModule writers) on all channels + # + def set_default_permission(cmd, val) + @default_perm.set_permission(Command.new(cmd), val) + debug "Default permissions now:\n#{@default_perm.inspect}" + end + + # default knows everybody + # def knows?(user) Irc::error_if_not_user(user) return true @@ -436,12 +468,24 @@ module Irc super add_netmask("*!*@*") end + + # DefaultBotUser will check the default_perm after checking + # the global ones + # or on all channels if _chan_ is nil + # + def permit?(cmd, chan=nil) + allow = super(cmd, chan) + if allow.nil? && chan.nil? + allow = @default_perm.permit?(cmd) + end + return allow + end end - # Returns the only instance of AnonBotUserClass + # Returns the only instance of DefaultBotUserClass # - def Auth.anonbotuser - return AnonBotUserClass.instance + def Auth.defaultbotuser + return DefaultBotUserClass.instance end # This is the BotOwner: he can do everything @@ -470,10 +514,15 @@ module Irc class AuthManagerClass include Singleton + attr_reader :everyone + attr_reader :botowner + # The instance manages two Hashes: one that maps # Irc::Users onto BotUsers, and the other that maps # usernames onto BotUser def initialize + @everyone = Auth::defaultbotuser + @botowner = Auth::botowner bot_associate(nil) end @@ -494,7 +543,9 @@ module Irc def reset_hashes @botusers = Hash.new @allbotusers = Hash.new - [Auth::anonbotuser, Auth::botowner].each { |x| @allbotusers[x.username.to_sym] = x } + [everyone, botowner].each { |x| + @allbotusers[x.username.to_sym] = x + } end # load botlist from userfile @@ -524,7 +575,8 @@ module Irc # Maps Irc::User to BotUser def irc_to_botuser(ircuser) Irc::error_if_not_user(ircuser) - return @botusers[ircuser] || Auth::anonbotuser + # TODO check netmasks + return @botusers[ircuser] || everyone end # creates a new BotUser @@ -569,8 +621,8 @@ module Irc # is returned: # * associated BotUser on _chan_ # * associated BotUser on all channels - # * anonbotuser on _chan_ - # * anonbotuser on all channels + # * everyone on _chan_ + # * everyone on all channels # def permit?(user, cmdtxt, chan=nil) botuser = irc_to_botuser(user) @@ -590,10 +642,10 @@ module Irc allow = botuser.permit?(cmd) return allow unless allow.nil? - unless botuser == Auth::anonbotuser - allow = Auth::anonbotuser.permit?(cmd, chan) if chan + unless botuser == everyone + allow = everyone.permit?(cmd, chan) if chan return allow unless allow.nil? - allow = Auth::anonbotuser.permit?(cmd) + allow = everyone.permit?(cmd) return allow unless allow.nil? end diff --git a/lib/rbot/config.rb b/lib/rbot/config.rb index ab16c442..f91cfa70 100644 --- a/lib/rbot/config.rb +++ b/lib/rbot/config.rb @@ -352,6 +352,9 @@ module Irc # bot:: parent bot class # create a new config hash from #{botclass}/conf.rbot + # TODO make this into a core module to guide a BotCOnfigManagerClass + # singleton instance from IRC + # def initialize(bot) @@bot = bot @@ -363,24 +366,24 @@ module Irc # unset # desc # and for arrays: - # add TODO - # remove TODO + # add + # remove @handler = MessageMapper.new(self) - @handler.map 'config list :module', :action => 'handle_list', + @handler.map 'config', 'config list :module', :action => 'handle_list', :defaults => {:module => false} - @handler.map 'config get :key', :action => 'handle_get' - @handler.map 'config desc :key', :action => 'handle_desc' - @handler.map 'config describe :key', :action => 'handle_desc' - @handler.map 'config set :key *value', :action => 'handle_set' - @handler.map 'config add :value to :key', :action => 'handle_add' - @handler.map 'config rm :value from :key', :action => 'handle_rm' - @handler.map 'config del :value from :key', :action => 'handle_rm' - @handler.map 'config delete :value from :key', :action => 'handle_rm' - @handler.map 'config unset :key', :action => 'handle_unset' - @handler.map 'config reset :key', :action => 'handle_unset' - @handler.map 'config help :topic', :action => 'handle_help', + @handler.map 'config', 'config get :key', :action => 'handle_get' + @handler.map 'config', 'config desc :key', :action => 'handle_desc' + @handler.map 'config', 'config describe :key', :action => 'handle_desc' + @handler.map 'config', 'config set :key *value', :action => 'handle_set' + @handler.map 'config', 'config add :value to :key', :action => 'handle_add' + @handler.map 'config', 'config rm :value from :key', :action => 'handle_rm' + @handler.map 'config', 'config del :value from :key', :action => 'handle_rm' + @handler.map 'config', 'config delete :value from :key', :action => 'handle_rm' + @handler.map 'config', 'config unset :key', :action => 'handle_unset' + @handler.map 'config', 'config reset :key', :action => 'handle_unset' + @handler.map 'config', 'config help :topic', :action => 'handle_help', :defaults => {:topic => false} - @handler.map 'help config :topic', :action => 'handle_help', + @handler.map 'config', 'help config :topic', :action => 'handle_help', :defaults => {:topic => false} if(File.exist?("#{@@bot.botclass}/conf.yaml")) diff --git a/lib/rbot/core/core.rb b/lib/rbot/core/core.rb index fcf5ac3a..55da1a7d 100644 --- a/lib/rbot/core/core.rb +++ b/lib/rbot/core/core.rb @@ -165,77 +165,78 @@ core = Core.new core.map "quit *msg", :action => 'bot_quit', :defaults => { :msg => nil }, - :auth => 'core::quit::quit' + :auth_path => 'quit' core.map "restart *msg", :action => 'bot_restart', :defaults => { :msg => nil }, - :auth => 'core::quit::restart' + :auth_path => 'quit' core.map "save", :action => 'bot_save', - :auth => 'core::config::save' + :auth_path => 'config' core.map "rescan", :action => 'bot_rescan', - :auth => 'core::config::rescan' + :auth_path => 'config' core.map "nick :nick", :action => 'bot_nick', - :auth => 'core::config::nick' + :auth_path => 'config' core.map "status", :action => 'bot_status', - :auth => 'core::config::show::status' + :auth_path => 'config::show' # TODO see above # # core.map "registry stats", # :action => 'bot_reg_stat', - # :auth => 'core::config::show::registry' + # :auth_path => 'config::show' core.map "version", :action => 'bot_version', - :auth => 'core::config::show::version' + :auth_path => 'config::show' core.map "quiet", :action => 'bot_quiet', - :auth => 'core::talk::quiet' + :auth_path => 'talk::set' core.map "quiet in :chan", :action => 'bot_quiet', - :auth => 'core::talk::quiet' + :auth_path => 'talk::set' core.map "talk", :action => 'bot_talk', - :auth => 'core::talk::talk' + :auth_path => 'talk::set' core.map "quiet in :chan", :action => 'bot_quiet', - :auth => 'core::talk::talk' + :auth_path => 'talk::set' + +core.map "say :where *what", + :action => 'bot_say', + :auth_path => 'talk::do' +core.map "action :where *what", + :action => 'bot_action', + :auth_path => 'talk::do' +core.map "mode :where :what *who", + :action => 'bot_mode', + :auth_path => 'talk::do' core.map "join :chan :pass", :action => 'bot_join', :defaults => {:pass => nil}, - :auth => 'core::movearound::join' + :auth_path => 'move' core.map "part :chan", :action => 'bot_part', :defaults => {:chan => nil}, - :auth => 'core::movearound::part' + :auth_path => 'move' core.map "hide", :action => 'bot_hide', - :auth => 'core::movearound::hide' - -core.map "say :where *what", - :action => 'bot_say', - :auth => 'core::talk::say' -core.map "action :where *what", - :action => 'bot_action', - :auth => 'core::talk::act' -core.map "mode :where :what *who", - :action => 'bot_mode', - :auth => 'core::talk::mode' + :auth_path => 'move' core.map "ping", - :action => 'bot_ping' + :action => 'bot_ping', + :auth_path => '!ping!' core.map "help *topic", :action => 'bot_help', - :default => { :topic => [""] } + :default => { :topic => [""] }, + :auth_path => '!help!' # TODO the first line should probably go to the auth module? # -core.default_auth('*', true) -core.default_auth('core', false) -core.default_auth('core::config::show', true) +core.default_auth('*', false) +core.default_auth('config::show', true) diff --git a/lib/rbot/ircbot.rb b/lib/rbot/ircbot.rb index f1d9e127..d96e0368 100644 --- a/lib/rbot/ircbot.rb +++ b/lib/rbot/ircbot.rb @@ -346,6 +346,7 @@ class IrcBot log_session_end exit 2 end + @auth.everyone.set_default_permission("*", true) Dir.mkdir("#{botclass}/plugins") unless File.exist?("#{botclass}/plugins") @plugins = Plugins::pluginmanager diff --git a/lib/rbot/messagemapper.rb b/lib/rbot/messagemapper.rb index 200c676d..e4ad60b0 100644 --- a/lib/rbot/messagemapper.rb +++ b/lib/rbot/messagemapper.rb @@ -93,13 +93,14 @@ module Irc # :requirements => {:limit => /^\d+$/}, # :private => false # - def map(*args) - @templates << Template.new(*args) + def map(botmodule, *args) + @templates << Template.new(botmodule, *args) end def each @templates.each {|tmpl| yield tmpl} end + def last @templates.last end @@ -125,7 +126,7 @@ module Irc failures << [tmpl, "class does not respond to action #{action}"] next end - auth = tmpl.options[:auth] ? tmpl.options[:auth] : tmpl.items[0] + auth = tmpl.options[:full_auth_path] debug "checking auth for #{auth}" if m.bot.auth.allow?(auth, m.source, m.replyto) debug "template match found and auth'd: #{action.inspect} #{options.inspect}" @@ -157,13 +158,48 @@ module Irc attr_reader :defaults # The defaults hash attr_reader :options # The options hash attr_reader :items - def initialize(template, hash={}) - raise ArgumentError, "Second argument must be a hash!" unless hash.kind_of?(Hash) + + def initialize(botmodule, template, hash={}) + raise ArgumentError, "Third argument must be a hash!" unless hash.kind_of?(Hash) @defaults = hash[:defaults].kind_of?(Hash) ? hash.delete(:defaults) : {} @requirements = hash[:requirements].kind_of?(Hash) ? hash.delete(:requirements) : {} self.items = template + if hash.has_key?(:auth) + warning "Command #{template} in #{botmodule} uses old :auth syntax, please upgrade" + end + if hash.has_key?(:full_auth_path) + warning "Command #{template} in #{botmodule} sets :full_auth_path, please don't do this" + else + case botmodule + when String + pre = botmodule + when Plugins::BotModule + pre = botmodule.name + else + raise ArgumentError, "Can't find auth base in #{botmodule.inspect}" + end + post = items.reject{ |x| + x == pre || x.kind_of?(Symbol) + } + if post.empty? + post = nil + else + post = post.first + end + if hash.has_key?(:auth_path) + extra = hash[:auth_path] + pre = nil if extra.sub!(/^!/, "") + post = nil if extra.sub!(/!$/, "") + else + extra = nil + end + hash[:full_auth_path] = [pre,extra,post].compact.join("::") + # TODO check if the full_auth_path is sane + end + @options = hash end + def items=(str) items = str.split(/\s+/).collect {|c| (/^(:|\*)(\w+)$/ =~ c) ? (($1 == ':' ) ? $2.intern : "*#{$2}".intern) : c} if str.kind_of?(String) # split and convert ':xyz' to symbols items.shift if items.first == "" diff --git a/lib/rbot/plugins.rb b/lib/rbot/plugins.rb index 31fb1134..7e98b1b3 100644 --- a/lib/rbot/plugins.rb +++ b/lib/rbot/plugins.rb @@ -112,7 +112,7 @@ module Plugins @handler = MessageMapper.new(self) @registry = BotRegistryAccessor.new(@bot, self.class.to_s.gsub(/^.*::/, "")) - @manager.add_botmodule(kl, self) + @manager.add_botmodule(self) end def flush_registry @@ -130,10 +130,11 @@ module Plugins end def map(*args) - @handler.map(*args) + @handler.map(self, *args) # register this map name = @handler.last.items[0] - self.register name + auth = @handler.last.options[:full_auth_path] + self.register name, :auth => auth unless self.respond_to?('privmsg') def self.privmsg(m) handle(m) @@ -142,10 +143,10 @@ module Plugins end def map!(*args) - @handler.map(*args) + @handler.map(self, *args) # register this map name = @handler.last.items[0] - self.register name, {:hidden => true} + self.register name, :auth => auth, :hidden => true unless self.respond_to?('privmsg') def self.privmsg(m) handle(m) @@ -153,12 +154,23 @@ module Plugins end end - # Sets the default auth for command _cmd_ to _val_ on channel _chan_: - # usually _chan_ is either "*" for everywhere, public and private (in - # which case it can be omitted) or "?" for private communications + # Sets the default auth for command path _cmd_ to _val_ on channel _chan_: + # usually _chan_ is either "*" for everywhere, public and private (in which + # case it can be omitted) or "?" for private communications # def default_auth(cmd, val, chan="*") - Auth::anonbotuser.set_permission(cmd, val) + case cmd + when "*", "" + c = nil + else + c = cmd + end + Auth::defaultbotuser.set_default_permission(propose_default_path(c), val) + end + + # Gets the default command path which would be given to command _cmd_ + def propose_default_path(cmd) + [name, cmd].compact.join("::") end # return an identifier for this plugin, defaults to a list of the message @@ -184,11 +196,15 @@ module Plugins # register the plugin as a handler for messages prefixed +name+ # this can be called multiple times for a plugin to handle multiple # message prefixes - def register(name, opts={}) + def register(cmd, opts={}) raise ArgumentError, "Second argument must be a hash!" unless opts.kind_of?(Hash) - return if @manager.knows?(name, @botmodule_class) - @manager.register(name, @botmodule_class, self) - @botmodule_triggers << name unless opts.fetch(:hidden, false) + return if @manager.knows?(cmd, @botmodule_class) + if opts.has_key?(:auth) + @manager.register(self, cmd, opts[:auth]) + else + @manager.register(self, cmd, propose_default_path(cmd)) + end + @botmodule_triggers << cmd unless opts.fetch(:hidden, false) end # default usage method provided as a utility for simple plugins. The @@ -249,14 +265,16 @@ module Plugins return @commandmappers[kl.to_sym].has_key?(name.to_sym) end - # Returns +true+ if _name_ is a known botmodule of class kl - def register(name, kl, botmodule) - raise TypeError, "Third argument #{botmodule.inspect} is not of class BotModule" unless botmodule.class <= BotModule - @commandmappers[kl.to_sym][name.to_sym] = botmodule + # Registers botmodule _botmodule_ with command _cmd_ and command path _auth_path_ + def register(botmodule, cmd, auth_path) + raise TypeError, "First argument #{botmodule.inspect} is not of class BotModule" unless botmodule.class <= BotModule + kl = botmodule.botmodule_class + @commandmappers[kl.to_sym][cmd.to_sym] = {:botmodule => botmodule, :auth => auth_path} end - def add_botmodule(kl, botmodule) - raise TypeError, "Second argument #{botmodule.inspect} is not of class BotModule" unless botmodule.class <= BotModule + def add_botmodule(botmodule) + raise TypeError, "Argument #{botmodule.inspect} is not of class BotModule" unless botmodule.class <= BotModule + kl = botmodule.botmodule_class raise "#{kl.to_s} #{botmodule.name} already registered!" if @botmodules[kl.to_sym].include?(botmodule) @botmodules[kl.to_sym] << botmodule end @@ -488,11 +506,12 @@ module Plugins # TODO should also check core_module and plugins [core_commands, plugin_commands].each { |pl| if(pl.has_key?(key)) + p = pl[key][:botmodule] begin - return pl[key].help(key, params) + return p.help(key, params) rescue Exception => err #rescue TimeoutError, StandardError, NameError, SyntaxError => err - error report_error("#{p.botmodule_class} #{plugins[key].name} help() failed:", err) + error report_error("#{p.botmodule_class} #{p.name} help() failed:", err) end else return false @@ -532,14 +551,16 @@ module Plugins # FIXME use fetch? k = m.plugin.to_sym if pl.has_key?(k) - p = pl[k] + p = pl[k][:botmodule] + a = pl[k][:auth] else p = nil + a = nil end if p # TODO This should probably be checked elsewhere debug "Checking auth ..." - if @bot.auth.allow?(m.plugin, m.source, m.replyto) + if @bot.auth.allow?(a, m.source, m.replyto) debug "Checking response ..." if p.respond_to?("privmsg") begin @@ -558,9 +579,9 @@ module Plugins debug "#{p.botmodule_class} #{p.name} is registered, but #{m.source} isn't allowed to use #{m.plugin} on #{m.replyto}" end else - debug "No #{pl.values.first.botmodule_class} registered #{m.plugin}" unless pl.empty? + debug "No #{pl.values.first[:botmodule].botmodule_class} registered #{m.plugin}" unless pl.empty? end - debug "Finished delegating privmsg with key #{m.plugin}" + ( pl.empty? ? "" : " to #{pl.values.first.botmodule_class}s" ) + debug "Finished delegating privmsg with key #{m.plugin}" + ( pl.empty? ? "" : " to #{pl.values.first[:botmodule].botmodule_class}s" ) } return false rescue Exception => e -- cgit v1.2.3