# HG changeset patch # User Goffi # Date 1569962951 -7200 # Node ID 9839ce068140c53dca7706d6c4dca2818e43ca61 # Parent 1761e4823527302a067aaffa790da53463bf4729 jp: password is now prompted if needed: if password is not specified on command line, it is now prompted if it is needed to start a profile session or connect it. The password is prompted with echo disabled, so somebody seing the computer screen can't see the password. fix 207 diff -r 1761e4823527 -r 9839ce068140 sat_frontends/jp/base.py --- a/sat_frontends/jp/base.py Tue Oct 01 22:49:11 2019 +0200 +++ b/sat_frontends/jp/base.py Tue Oct 01 22:49:11 2019 +0200 @@ -46,6 +46,7 @@ import sat_frontends.jp from sat_frontends.jp.loops import QuitException, getJPLoop from sat_frontends.jp.constants import Const as C +from sat_frontends.bridge.bridge_frontend import BridgeException from sat_frontends.tools import misc import xml.etree.ElementTree as ET # FIXME: used temporarily to manage XMLUI from collections import OrderedDict @@ -288,7 +289,7 @@ "-p", "--profile", action="store", type=str, default='@DEFAULT@', help=_("Use PROFILE profile key (default: %(default)s)")) parent.add_argument( - "--pwd", action="store", default='', metavar='PASSWORD', + "--pwd", action="store", metavar='PASSWORD', help=_("Password used to connect profile, if necessary")) profile_parent, profile_session_parent = (self.parents['profile'], @@ -773,6 +774,54 @@ return dest_jids + async def a_pwd_input(self, msg=''): + """Like ainput but with echo disabled (useful for passwords)""" + # we disable echo, code adapted from getpass standard module which has been + # written by Piers Lauder (original), Guido van Rossum (Windows support and + # cleanup) and Gregory P. Smith (tty support & GetPassWarning), a big thanks + # to them (and for all the amazing work on Python). + stdin_fd = sys.stdin.fileno() + old = termios.tcgetattr(sys.stdin) + new = old[:] + new[3] &= ~termios.ECHO + tcsetattr_flags = termios.TCSAFLUSH + if hasattr(termios, 'TCSASOFT'): + tcsetattr_flags |= termios.TCSASOFT + try: + termios.tcsetattr(stdin_fd, tcsetattr_flags, new) + pwd = await self.ainput(msg=msg) + finally: + termios.tcsetattr(stdin_fd, tcsetattr_flags, old) + sys.stderr.flush() + self.disp('') + return pwd + + async def connectOrPrompt(self, method, err_msg=None): + """Try to connect/start profile session and prompt for password if needed + + @param method(callable): bridge method to either connect or start profile session + It will be called with password as sole argument, use lambda to do the call + properly + @param err_msg(str): message to show if connection fail + """ + password = self.args.pwd + while True: + try: + await method(password or '') + except Exception as e: + if ((isinstance(e, BridgeException) + and e.classname == 'PasswordError' + and self.args.pwd is None)): + if password is not None: + self.disp(A.color(C.A_WARNING, _("invalid password"))) + password = await self.a_pwd_input( + _("please enter profile password:")) + else: + self.disp(err_msg.format(profile=self.profile, e=e), error=True) + self.quit(C.EXIT_ERROR) + else: + break + async def connect_profile(self): """Check if the profile is connected and do it if requested @@ -794,11 +843,10 @@ pass else: if start_session: - try: - await self.bridge.profileStartSession(self.args.pwd, self.profile) - except Exception as e: - self.disp(_(f"Can't start {self.profile}'s session: {e}"), err=True) - self.quit(1) + await self.connectOrPrompt( + lambda pwd: self.bridge.profileStartSession(pwd, self.profile), + err_msg="Can't start {profile}'s session: {e}" + ) return elif not await self.bridge.profileIsSessionStarted(self.profile): if not self.args.connect: @@ -816,11 +864,10 @@ # creation/deletion) return elif self.args.connect is True: # if connection is asked, we connect the profile - try: - await self.bridge.connect(self.profile, self.args.pwd, {}) - except Exception as e: - self.disp(_(f"Can't connect profile: {e}"), error=True) - self.quit(1) + await self.connectOrPrompt( + lambda pwd: self.bridge.connect(self.profile, pwd, {}), + err_msg = 'Can\'t connect profile "{profile!s}": {e}' + ) return else: if not await self.bridge.isConnected(self.profile):