jenkins-bot has submitted this change and it was merged.
Change subject: Client side write API assert and warning ......................................................................
Client side write API assert and warning
If the username is not present or is a IP address, raise an error without sending a request to the server.
Warn if the site user is not a config specified username.
Change-Id: I5074bdb14c1cdc37e2ac2b5a71887c1b699f95b4 --- M pywikibot/data/api.py M tests/dry_api_tests.py 2 files changed, 57 insertions(+), 2 deletions(-)
Approvals: John Vandenberg: Looks good to me, but someone else must approve XZise: Looks good to me, approved jenkins-bot: Verified
diff --git a/pywikibot/data/api.py b/pywikibot/data/api.py index 741a75b..bc3c42a 100644 --- a/pywikibot/data/api.py +++ b/pywikibot/data/api.py @@ -25,7 +25,7 @@
import pywikibot from pywikibot import config, login -from pywikibot.tools import MediaWikiVersion, deprecated, itergroup +from pywikibot.tools import MediaWikiVersion, deprecated, itergroup, ip from pywikibot.exceptions import ( Server504Error, Server414Error, FatalServerError, Error ) @@ -800,6 +800,22 @@ "wbcreateclaim", "wbremoveclaims", "wbsetclaimvalue", "wbsetreference", "wbremovereferences" ) + # Client side verification that the request is being performed + # by a logged in user, and warn if it isn't a config username. + if self.write: + if not hasattr(self.site, "_userinfo"): + raise Error(u"API write action attempted without userinfo") + assert('name' in self.site._userinfo) + + if ip.is_IP(self.site._userinfo['name']): + raise Error(u"API write action attempted as IP %r" + % self.site._userinfo['name']) + + if not self.site.user(): + pywikibot.warning( + u"API write action by unexpected username commenced.\n" + u"userinfo: %r" % self.site._userinfo) + # MediaWiki 1.23 allows assertion for any action, # whereas earlier WMF wikis and others used an extension which # could only allow assert for action=edit. diff --git a/tests/dry_api_tests.py b/tests/dry_api_tests.py index eb260c1..fa78901 100644 --- a/tests/dry_api_tests.py +++ b/tests/dry_api_tests.py @@ -180,6 +180,40 @@ self.assertEqual(en_user_path, ar_user_path)
+class DryWriteAssertTests(DefaultDrySiteTestCase): + + """Test client site write assert.""" + + def test_no_user(self): + """Test Request object when not a user.""" + site = self.get_site() + + del site._userinfo + self.assertRaisesRegex(pywikibot.Error, ' without userinfo', + Request, site=site, action='edit') + + site._userinfo = {'name': '1.2.3.4', 'groups': []} + + self.assertRaisesRegex(pywikibot.Error, " as IP '1.2.3.4'", + Request, site=site, action='edit') + + def test_unexpected_user(self): + """Test Request object when username is not correct.""" + site = self.get_site() + site._userinfo = {'name': 'other_username', 'groups': []} + site._username[0] = 'myusername' + + Request(site=site, action='edit') + + def test_normal(self): + """Test Request object when username is correct.""" + site = self.get_site() + site._userinfo = {'name': 'myusername', 'groups': []} + site._username[0] = 'myusername' + + Request(site=site, action='edit') + + class DryMimeTests(TestCase):
"""Test MIME request handling without a real site.""" @@ -197,6 +231,7 @@ self.assertEqual(file_content, submsg.get_payload(decode=True))
def test_mime_file_container(self): + """Test Request._build_mime_request encodes binary.""" local_filename = os.path.join(_images_dir, 'MP_sounds.png') with open(local_filename, 'rb') as f: file_content = f.read() @@ -213,7 +248,11 @@
def test_upload_object(self): """Test Request object prepared to upload.""" - req = Request(site=self.get_site(), action="upload", + # fake write test needs the config username + site = self.get_site() + site._username[0] = 'myusername' + site._userinfo = {'name': 'myusername', 'groups': []} + req = Request(site=site, action="upload", file='MP_sounds.png', mime=True, filename=os.path.join(_images_dir, 'MP_sounds.png')) self.assertEqual(req.mime, True)