From 2d8bbfc245c06b79c74265a3c73ce6a461e32401 Mon Sep 17 00:00:00 2001 From: Eli Clemente Gordillo Foster Date: Tue, 18 Oct 2016 13:26:57 -0700 Subject: [PATCH] :gem: Add support for the Assert API (#51) * New :assertion option in initialize opts * New override_assertion parameter in post to override the assert parameter and not throw an error. * New NotLoggedInError and NotBotError for the according assertions * user_bot? no longer has a parameter. It now queries with an assertion rather than checking user groups. * New logged_in? method to check if the user is currently logged in. --- lib/mediawiki/auth.rb | 4 ++-- lib/mediawiki/butt.rb | 48 ++++++++++++++++++++++++++++++------- lib/mediawiki/exceptions.rb | 2 ++ spec/util_test.rb | 16 ++++++++----- 4 files changed, 53 insertions(+), 17 deletions(-) diff --git a/lib/mediawiki/auth.rb b/lib/mediawiki/auth.rb index 91357bf..9ab776b 100644 --- a/lib/mediawiki/auth.rb +++ b/lib/mediawiki/auth.rb @@ -22,7 +22,7 @@ def login(username, password, token = nil) header = {} header['Set-Cookie'] = @cookie if @cookie - response = post(params, true, header) + response = post(params, true, header, true) result = response['login']['result'] if result == 'NeedToken' @@ -50,7 +50,7 @@ def logout action: 'logout' } - post(params) + post(params, true, nil, true) @logged_in = false true diff --git a/lib/mediawiki/butt.rb b/lib/mediawiki/butt.rb index 63f71f0..e10e3a2 100644 --- a/lib/mediawiki/butt.rb +++ b/lib/mediawiki/butt.rb @@ -25,6 +25,7 @@ class Butt attr_accessor :query_limit_default attr_accessor :use_continuation + attr_accessor :assertion # Creates a new instance of MediaWiki::Butt. # @param url [String] The FULL wiki URL. api.php can be omitted, but it will make harsh assumptions about @@ -36,6 +37,10 @@ class Butt # use that, otherwise, it will use this. Defaults to 500. It can be set to 'max' to use MW's default max for # each API. # @option opts [Boolean] :use_continuation Whether to use the continuation API on queries. + # @option opts [Symbol] :assertion If set to :user or :bot, will use the assert parameter in all requests. + # Setting this will open up the possibility for NotLoggedInErrors and NotBotErrors. It is important to keep in + # mind that methods that check if the user is logged in do not use the API, but check if the user has *ever* + # logged in as this Butt instance. In other words, it is a safety check for performance and not a valid API check. def initialize(url, opts = {}) @url = url =~ /api.php$/ ? url : "#{url}/api.php" @query_limit_default = opts[:query_limit_default] || 500 @@ -44,6 +49,9 @@ def initialize(url, opts = {}) @logged_in = false @custom_agent = opts[:custom_agent] @use_continuation = opts[:use_continuation] + + assertion = opts[:assertion] + @assertion = assertion == :user || assertion == :bot ? assertion : nil end # Performs a generic HTTP POST request and provides the response. This method generally should not be used by the @@ -52,11 +60,14 @@ def initialize(url, opts = {}) # information. # @param autoparse [Boolean] Whether or not to provide a parsed version of the response's JSON. # @param header [Hash] The header hash. Optional. + # @param override_assertion [Boolean] Whether to override the @assertion check. This is used in #login because + # that can never be done while already logged in. # @since 0.1.0 # @return [Hash] Parsed JSON if autoparse is true. # @return [HTTPMessage] Raw HTTP response if autoparse is not true. - def post(params, autoparse = true, header = nil) + def post(params, autoparse = true, header = nil, override_assertion = false) params[:format] = 'json' + params[:assert] = @assertion.to_s if @assertion && !override_assertion && !params.key?(:assert) header = {} if header.nil? header['User-Agent'] = @logged_in ? "#{@name}/MediaWiki::Butt" : 'NotLoggedIn/MediaWiki::Butt' @@ -64,7 +75,15 @@ def post(params, autoparse = true, header = nil) res = @client.post(@uri, params, header) - autoparse ? JSON.parse(res.body) : res + return res unless autoparse + parsed = JSON.parse(res.body) + + if !override_assertion || @assertion + code = parsed.dig('error', 'code') + fail MediaWiki::Butt::NotLoggedInError.new(parsed['error']['info']) if code == 'assertuserfailed' + fail MediaWiki::Butt::NotBotError.new(parsed['error']['info']) if code == 'assertbotfailed' + end + parsed end # Performs a Mediawiki API query and provides the response, dealing with continuation as necessary. @@ -112,18 +131,29 @@ def query_ary(params, base_response_key, property_key) end # Gets whether the currently logged in user is a bot. - # @param username [String] The username to check. Optional. Defaults to - # the currently logged in user if nil. # @return [Boolean] true if logged in as a bot, false if not logged in or # logged in as a non-bot # @since 0.1.0 as is_current_user_bot # @since 0.3.0 as is_user_bot? # @since 0.4.1 as user_bot? - def user_bot?(username = nil) - groups = false - name = username || @name - groups = get_usergroups(name) if name - groups && groups.include?('bot') + def user_bot? + begin + post({ action: 'query', assert: 'bot' }) + true + rescue MediaWiki::Butt::NotBotError + false + end + end + + # Checks whether this instance is logged in. + # @return [Boolean] true if logged in, false if not. + def logged_in? + begin + post({ action: 'query', assert: 'user' }) + true + rescue MediaWiki::Butt::NotLoggedInError + false + end end protected diff --git a/lib/mediawiki/exceptions.rb b/lib/mediawiki/exceptions.rb index 7867803..3e378f3 100644 --- a/lib/mediawiki/exceptions.rb +++ b/lib/mediawiki/exceptions.rb @@ -3,5 +3,7 @@ class Butt class AuthenticationError < StandardError; end class EditError < StandardError; end class BlockError < StandardError; end + class NotLoggedInError < StandardError; end + class NotBotError < StandardError; end end end diff --git a/spec/util_test.rb b/spec/util_test.rb index 328a80a..8ae4907 100644 --- a/spec/util_test.rb +++ b/spec/util_test.rb @@ -38,15 +38,19 @@ class Butt describe 'MediaWiki::Query' do describe '#get_limited' do it 'uses default maximums to limit the value' do - MW_BUTT_NO_URL.get_limited(500).must_equal(500) - MW_BUTT_NO_URL.get_limited(400).must_equal(400) - MW_BUTT_NO_URL.get_limited(600).must_equal(500) + MW_BUTT_NO_URL.stub(:user_bot?, false) do + MW_BUTT_NO_URL.get_limited(500).must_equal(500) + MW_BUTT_NO_URL.get_limited(400).must_equal(400) + MW_BUTT_NO_URL.get_limited(600).must_equal(500) + end end it 'uses custom user maximum values to limit the value' do - MW_BUTT_NO_URL.get_limited(400, 600).must_equal(400) - MW_BUTT_NO_URL.get_limited(600, 600).must_equal(600) - MW_BUTT_NO_URL.get_limited(700, 600).must_equal(600) + MW_BUTT_NO_URL.stub(:user_bot?, false) do + MW_BUTT_NO_URL.get_limited(400, 600).must_equal(400) + MW_BUTT_NO_URL.get_limited(600, 600).must_equal(600) + MW_BUTT_NO_URL.get_limited(700, 600).must_equal(600) + end end it 'limits the value while using a bot account' do