Skip to content

Commit c0cf17c

Browse files
committed
[mw 1.37] Fix for removed action API token parameters
The legacy API parameters have been finally removed[1], which means that we can no longer check them using action=paraminfo. - always use 'csrf' token for outdated token types (mw version >= 1.24wmf19) with TokenWallet - log a message if a given token was replaced inside TokenWallet - validate tokens by ignoring outdated tokens because they are replaced by'csrf' already - update get_tokens doc string - update token_tests [1] https://www.mediawiki.org/wiki/MediaWiki_1.37/Deprecation_of_legacy_API_token_parameters Bug: T291202 Change-Id: Ic3c0e629f6bd4a609679e0c3a100a75a497713ef
1 parent 5a9b53a commit c0cf17c

File tree

3 files changed

+34
-31
lines changed

3 files changed

+34
-31
lines changed

pywikibot/site/_apisite.py

Lines changed: 6 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1294,28 +1294,14 @@ def getredirtarget(self, page):
12941294

12951295
return page._redirtarget
12961296

1297-
def validate_tokens(self, types: List[str]):
1297+
def validate_tokens(self, types: List[str]) -> List[str]:
12981298
"""Validate if requested tokens are acceptable.
12991299
13001300
Valid tokens depend on mw version.
13011301
"""
1302-
mw_ver = self.mw_version
1303-
if mw_ver < '1.24wmf19':
1304-
types_wiki = self._paraminfo.parameter('tokens', 'type')['type']
1305-
valid_types = [token for token in types if token in types_wiki]
1306-
else:
1307-
types_wiki_old = self._paraminfo.parameter('query+info',
1308-
'token')['type']
1309-
types_wiki_action = self._paraminfo.parameter('tokens',
1310-
'type')['type']
1311-
types_wiki = self._paraminfo.parameter('query+tokens',
1312-
'type')['type']
1313-
valid_types = [token for token in types if token in types_wiki]
1314-
for token in types:
1315-
if (token in types_wiki_old or token in types_wiki_action) \
1316-
and token not in valid_types:
1317-
valid_types.append('csrf')
1318-
return valid_types
1302+
query = 'tokens' if self.mw_version < '1.24wmf19' else 'query+tokens'
1303+
token_types = self._paraminfo.parameter(query, 'type')['type']
1304+
return [token for token in types if token in token_types]
13191305

13201306
def get_tokens(self, types: List[str], all: bool = False) -> dict:
13211307
"""Preload one or multiple tokens.
@@ -1327,7 +1313,9 @@ def get_tokens(self, types: List[str], all: bool = False) -> dict:
13271313
the parameter is not known it will default to the 'csrf' token.
13281314
13291315
The other token types available are:
1316+
- createaccount
13301317
- deleteglobalaccount
1318+
- login
13311319
- patrol
13321320
- rollback
13331321
- setglobalaccountstatus

pywikibot/site/_tokenwallet.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#
55
# Distributed under the terms of the MIT license.
66
#
7+
from pywikibot import log
78
from pywikibot.exceptions import Error
89

910

@@ -54,6 +55,14 @@ def __getitem__(self, key):
5455
# always preload all for users without tokens
5556
failed_cache_key = (self.site.user(), key)
5657

58+
# redirect old tokens to be compatible with older MW version
59+
# https://www.mediawiki.org/wiki/MediaWiki_1.37/Deprecation_of_legacy_API_token_parameters
60+
if self.site.mw_version >= '1.24wmf19' \
61+
and key in {'edit', 'delete', 'protect', 'move', 'block', 'unblock',
62+
'email', 'import', 'options'}:
63+
log('Token {!r} was replaced by {!r}'.format(key, 'csrf'))
64+
key = 'csrf'
65+
5766
try:
5867
key = self.site.validate_tokens([key])[0]
5968
except IndexError:

tests/token_tests.py

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from contextlib import suppress
88

99
from pywikibot.exceptions import APIError, Error
10+
from pywikibot.tools import MediaWikiVersion
1011
from pywikibot.site import TokenWallet
1112
from tests.aspects import DefaultSiteTestCase, TestCase, TestCaseBase, unittest
1213

@@ -45,31 +46,36 @@ def _test_tokens(self, version, test_version, additional_token):
4546
.format(self.mysite, self._version))
4647

4748
self.mysite.version = lambda: test_version
49+
del self.mysite._mw_version_time # remove cached mw_version
4850

49-
for ttype in ('edit', 'move', 'delete', 'patrol', additional_token):
50-
tokentype = self.mysite.validate_tokens([ttype])
51+
redirected_tokens = ['edit', 'move', 'delete']
52+
for ttype in redirected_tokens + ['patrol', additional_token]:
5153
try:
5254
token = self.mysite.tokens[ttype]
5355
except Error as error_msg:
54-
if tokentype:
55-
self.assertRegex(
56-
str(error_msg),
57-
"Action '[a-z]+' is not allowed "
58-
'for user .* on .* wiki.')
59-
# test __contains__
60-
self.assertNotIn(tokentype[0], self.mysite.tokens)
56+
if self.mysite.validate_tokens([ttype]):
57+
pattern = ("Action '[a-z]+' is not allowed "
58+
'for user .* on .* wiki.')
6159
else:
62-
self.assertRegex(
63-
str(error_msg),
64-
"Requested token '[a-z]+' is invalid on .* wiki.")
60+
pattern = "Requested token '[a-z]+' is invalid on .* wiki."
61+
62+
self.assertRegex(str(error_msg), pattern)
63+
6564
else:
6665
self.assertIsInstance(token, str)
6766
self.assertEqual(token, self.mysite.tokens[ttype])
6867
# test __contains__
69-
self.assertIn(tokentype[0], self.mysite.tokens)
68+
if test_version < '1.24wmf19':
69+
self.assertIn(ttype, self.mysite.tokens)
70+
elif ttype in redirected_tokens:
71+
self.assertEqual(self.mysite.tokens[ttype],
72+
self.mysite.tokens['csrf'])
7073

7174
def test_tokens_in_mw_123_124wmf18(self):
7275
"""Test ability to get page tokens."""
76+
if MediaWikiVersion(self.orig_version()) >= '1.37wmf24':
77+
self.skipTest('Site {} version {} is too new for this tests.'
78+
.format(self.mysite, self._version))
7379
self._test_tokens('1.23', '1.24wmf18', 'deleteglobalaccount')
7480

7581
def test_tokens_in_mw_124wmf19(self):

0 commit comments

Comments
 (0)