jenkins-bot has submitted this change and it was merged. ( https://gerrit.wikimedia.org/r/350448 )
Change subject: [IMPR] Add BotPasswords in generate_user_files.py ......................................................................
[IMPR] Add BotPasswords in generate_user_files.py
Support it by generating a user-password.py file
For each different username entered, ask if user wants to save a BotPassword (BotPassword name, BotPassword pass) and save it in a user-password.py file that contain (u'myusername', BotPassword(u'mybotpasswordname', u'mysecretbotpasswordpass'))
Doc: https://www.mediawiki.org/wiki/Manual:Pywikibot/BotPasswords
Bug: T143905 Change-Id: I35e30b6b94a360c16d365499c78863ac3aac1663 --- M generate_user_files.py M pywikibot/config2.py M pywikibot/tools/__init__.py 3 files changed, 98 insertions(+), 11 deletions(-)
Approvals: Xqt: Looks good to me, approved jenkins-bot: Verified
diff --git a/generate_user_files.py b/generate_user_files.py index 4de8a47..977dcc1 100755 --- a/generate_user_files.py +++ b/generate_user_files.py @@ -204,20 +204,43 @@ # family , you can use '*' {usernames}
+# The list of BotPasswords is saved in another file. Import it if needed. +# See https://www.mediawiki.org/wiki/Manual:Pywikibot/BotPasswords to know how +# use them. +{botpasswords}
{config_text}"""
SMALL_CONFIG = ('# -*- coding: utf-8 -*-\n' - u"from __future__ import absolute_import, unicode_literals\n" - u"family = '{main_family}'\n" + 'from __future__ import absolute_import, unicode_literals\n' + "family = '{main_family}'\n" "mylang = '{main_code}'\n" - u"{usernames}\n") + '{usernames}\n' + '{botpasswords}\n') + +PASSFILE_CONFIG = """# This is an automatically generated file used to store +# BotPasswords. +# +# As a simpler (but less secure) alternative to OAuth, MediaWiki allows bot +# users to uses BotPasswords to limit the permissions given to a bot. +# When using BotPasswords, each instance gets keys. This combination can only +# access the API, not the normal web interface. +# +# See https://www.mediawiki.org/wiki/Manual:Pywikibot/BotPasswords for more +# information. +{botpasswords}"""
def create_user_config(args=None, force=False): - """Create a user-config.py in base_dir.""" + """ + Create a user-config.py in base_dir. + + Create a user-password.py if necessary. + """ _fnc = os.path.join(base_dir, "user-config.py") - if file_exists(_fnc): + _fncpass = os.path.join(base_dir, 'user-password.py') + # TODO: T167573: better check for existing user-password file + if file_exists(_fnc) or file_exists(_fncpass): return
if args and force and not config.verbose_output: @@ -235,13 +258,48 @@ usernames += [get_site_and_lang(main_family, main_code, main_username)]
+ botpasswords = [] if not main_username: usernames = "# usernames['{0}']['{1}'] = u'MyUsername'".format( main_family, main_code) else: + # For each different username entered, ask if user wants to save a + # BotPassword (username, BotPassword name, BotPassword pass) + seen = set() + for username in usernames: + if username[2] in seen: + continue + seen.add(username[2]) + if pywikibot.input_yn('Do you want to add a BotPassword for {0}?' + .format(username[2]), + force=force, default=False): + if not botpasswords: + pywikibot.output( + 'See https://www.mediawiki.org/wiki/' + 'Manual:Pywikibot/BotPasswords to know ' + 'how to get codes.') + pywikibot.output('Please note that plain text in {0} and ' + 'anyone with read access to that ' + 'directory will be able read the file.' + .format(_fncpass)) + message = 'BotPassword's "bot name" for {0}'.format( + username[2]) + botpasswordname = pywikibot.input(message, force=force) + message = 'BotPassword's "password" for BotPassword "{0}" ' \ + '(no characters will be shown)' \ + .format(botpasswordname) + botpasswordpass = pywikibot.input(message, force=force, + password=True) + if botpasswordname and botpasswordpass: + botpasswords.append((username[2], botpasswordname, + botpasswordpass)) + usernames = '\n'.join( u"usernames['{0}']['{1}'] = u'{2}'".format(*username) for username in usernames) + botpasswords = '\n'.join( + "('{0}', BotPassword('{1}', '{2}'))".format(*botpassword) + for botpassword in botpasswords)
config_text = '' config_content = SMALL_CONFIG @@ -288,12 +346,16 @@ pywikibot.exception()
try: + # Finally save user-config.py with codecs.open(_fnc, "w", "utf-8") as f: f.write(config_content.format(main_family=main_family, main_code=main_code, usernames=usernames, - config_text=config_text)) - + config_text=config_text, + botpasswords='password_file = ' + + ('"user-password.py"' + if botpasswords + else 'None'))) pywikibot.output(u"'%s' written." % _fnc) except: try: @@ -301,6 +363,23 @@ except: pass raise + + if botpasswords: + # Save if necessary user-password.py + try: + # First create an empty file with good permissions, before writing + # in it + with codecs.open(_fncpass, 'w', 'utf-8') as f: + f.write('') + pywikibot.tools.file_mode_checker(_fncpass, mode=0o600, + quiet=True) + with codecs.open(_fncpass, 'w', 'utf-8') as f: + f.write(PASSFILE_CONFIG.format(botpasswords=botpasswords)) + pywikibot.tools.file_mode_checker(_fncpass, mode=0o600) + pywikibot.output("'{0}' written.".format(_fncpass)) + except EnvironmentError: + os.remove(_fncpass) + raise
def main(*args): @@ -341,6 +420,7 @@ # Only give option for directory change if user-config.py already exists # in the directory. This will repeat if user-config.py also exists in # the requested directory. + # TODO: T167573: check for user-password.py too if not force or config.verbose_output: pywikibot.output(u'\nYour default user directory is "%s"' % base_dir) while os.path.isfile(os.path.join(base_dir, "user-config.py")): diff --git a/pywikibot/config2.py b/pywikibot/config2.py index 78898cc..97203a3 100644 --- a/pywikibot/config2.py +++ b/pywikibot/config2.py @@ -218,11 +218,18 @@ # By default you are asked for a password on the terminal. # A password file may be used, e.g. password_file = ".passwd". # The path to the password file is relative to that of the user_config file. -# The password file should consist of lines containing -# Python tuples of any of the following formats: +# The password file should consist of lines containing Python tuples of any +# of the following formats: # (code, family, username, password) # (family, username, password) # (username, password) +# It's also possible (and safer) for bot users to use BotPasswords to limit +# the permissions given to a bot. When using BotPasswords, each instance gets +# keys. This combination can only access the API, not the normal web interface. +# See https://www.mediawiki.org/wiki/Manual:Pywikibot/BotPasswords to know how +# use them. In this case, the password file should contein a BotPassword object +# in the following format: +# (username, BotPassword(botname, botpassword)) password_file = None
# edit summary to use if not supplied by bot script diff --git a/pywikibot/tools/__init__.py b/pywikibot/tools/__init__.py index 7e1c9c1..4546304 100644 --- a/pywikibot/tools/__init__.py +++ b/pywikibot/tools/__init__.py @@ -1709,7 +1709,7 @@ return open_archive(filename, use_extension=use_extension)
-def file_mode_checker(filename, mode=0o600): +def file_mode_checker(filename, mode=0o600, quiet=False): """Check file mode and update it, if needed.
@param filename: filename path @@ -1723,7 +1723,7 @@ if stat.S_ISREG(st_mode) and (st_mode - stat.S_IFREG != mode): os.chmod(filename, mode) # re-read and check changes - if os.stat(filename).st_mode != st_mode: + if os.stat(filename).st_mode != st_mode and not quiet: warn(warn_str.format(filename, st_mode - stat.S_IFREG, mode))
pywikibot-commits@lists.wikimedia.org