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
This commit is contained in:
Goffi 2019-10-01 22:49:11 +02:00
commit 36ccde03b9

View file

@ -46,6 +46,7 @@ from sat.core import exceptions
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 @@ class Jp(object):
"-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 @@ class Jp(object):
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 @@ class Jp(object):
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 @@ class Jp(object):
# 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):