Skip to content

Commit e77def3

Browse files
committed
Show user in password prompt (fixing regression)
In order to do this, we have to lose some special DSN logic. There is just no way under the current setup, since for instance the SSL params get parsed out of the DSN before calling connect(). Once solution could be to do all configuration parsing early, a long- term goal. Here we replace the DSN fallthrough with a warning message.
1 parent 7213203 commit e77def3

File tree

3 files changed

+47
-50
lines changed

3 files changed

+47
-50
lines changed

changelog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ Bug Fixes
1111
--------
1212
* Refactor completions for special commands, with minor casing fixes.
1313
* Raise `--password-file` higher in the precedence of password specification.
14+
* Fix regression: show username in password prompt.
1415

1516

1617
Internal

mycli/main.py

Lines changed: 44 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -480,6 +480,7 @@ def connect(
480480
ssh_key_filename: str | None = "",
481481
init_command: str | None = "",
482482
unbuffered: bool | None = None,
483+
password_file: str | None = None,
483484
) -> None:
484485
cnf = {
485486
"database": None,
@@ -538,9 +539,50 @@ def connect(
538539
# 4. DSN (mysql://user:password)
539540
# 5. cnf (.my.cnf / etc)
540541

542+
def get_password_from_file(password_file: str | None) -> str | None:
543+
if not password_file:
544+
return None
545+
try:
546+
with open(password_file) as fp:
547+
password = fp.readline().strip()
548+
return password
549+
except FileNotFoundError:
550+
click.secho(f"Password file '{password_file}' not found", err=True, fg="red")
551+
sys.exit(1)
552+
except PermissionError:
553+
click.secho(f"Permission denied reading password file '{password_file}'", err=True, fg="red")
554+
sys.exit(1)
555+
except IsADirectoryError:
556+
click.secho(f"Path '{password_file}' is a directory, not a file", err=True, fg="red")
557+
sys.exit(1)
558+
except Exception as e:
559+
click.secho(f"Error reading password file '{password_file}': {str(e)}", err=True, fg="red")
560+
sys.exit(1)
561+
562+
if passwd and '://' in passwd:
563+
is_valid_scheme, _scheme = is_valid_connection_scheme(passwd or '')
564+
if is_valid_scheme:
565+
click.secho('Warning: password looks like a DSN. Check the command line.', err=True, fg='yellow')
566+
567+
# if user passes the --p* flag, ask for the password right away
568+
# to enforce consistency and reduce lag as much as possible
569+
if passwd == "MYCLI_ASK_PASSWORD":
570+
passwd = click.prompt(f"Enter password for {user}", hide_input=True, show_default=False, default='', type=str, err=True)
571+
572+
# if the passwd is not specified try to set it using the password_file option
573+
if passwd is None and password_file:
574+
password_from_file = get_password_from_file(password_file)
575+
if password_from_file is not None:
576+
passwd = password_from_file
577+
578+
# getting the envvar ourselves because the envvar from a click
579+
# option cannot be an empty string, but a password can be
580+
if passwd is None and os.environ.get("MYSQL_PWD") is not None:
581+
passwd = os.environ.get("MYSQL_PWD")
582+
541583
# if no password was found from all of the above sources, ask for a password
542584
if passwd is None:
543-
passwd = click.prompt("Enter password", hide_input=True, show_default=False, default='', type=str, err=True)
585+
passwd = click.prompt(f"Enter password for {user}", hide_input=True, show_default=False, default='', type=str, err=True)
544586

545587
# Connect to the database.
546588
def _connect() -> None:
@@ -1600,54 +1642,6 @@ def cli(
16001642
- mycli mysql://my_user@my_host.com:3306/my_database
16011643
16021644
"""
1603-
1604-
def get_password_from_file(password_file: str | None) -> str | None:
1605-
if not password_file:
1606-
return None
1607-
try:
1608-
with open(password_file) as fp:
1609-
password = fp.readline().strip()
1610-
return password
1611-
except FileNotFoundError:
1612-
click.secho(f"Password file '{password_file}' not found", err=True, fg="red")
1613-
sys.exit(1)
1614-
except PermissionError:
1615-
click.secho(f"Permission denied reading password file '{password_file}'", err=True, fg="red")
1616-
sys.exit(1)
1617-
except IsADirectoryError:
1618-
click.secho(f"Path '{password_file}' is a directory, not a file", err=True, fg="red")
1619-
sys.exit(1)
1620-
except Exception as e:
1621-
click.secho(f"Error reading password file '{password_file}': {str(e)}", err=True, fg="red")
1622-
sys.exit(1)
1623-
1624-
# if user passes the --p* flag, ask for the password right away
1625-
# to reduce lag as much as possible
1626-
if password == "MYCLI_ASK_PASSWORD":
1627-
password = click.prompt("Enter password", hide_input=True, show_default=False, default='', type=str, err=True)
1628-
# if the password value looks like a DSN, treat it as such and
1629-
# prompt for password
1630-
elif database is None and password is not None and "://" in password:
1631-
# check if the scheme is valid. We do not actually have any logic for these, but
1632-
# it will most usefully catch the case where we erroneously catch someone's
1633-
# password, and give them an easy error message to follow / report
1634-
is_valid_scheme, scheme = is_valid_connection_scheme(password)
1635-
if not is_valid_scheme:
1636-
click.secho(f"Error: Unknown connection scheme provided for DSN URI ({scheme}://)", err=True, fg="red")
1637-
sys.exit(1)
1638-
database = password
1639-
password = click.prompt("Enter password", hide_input=True, show_default=False, default='', type=str, err=True)
1640-
1641-
# if the passwd is not specified try to set it using the password_file option
1642-
if password is None and password_file:
1643-
if password_from_file := get_password_from_file(password_file):
1644-
password = password_from_file
1645-
1646-
# getting the envvar ourselves because the envvar from a click
1647-
# option cannot be an empty string, but a password can be
1648-
if password is None and os.environ.get("MYSQL_PWD") is not None:
1649-
password = os.environ.get("MYSQL_PWD")
1650-
16511645
mycli = MyCli(
16521646
prompt=prompt,
16531647
logfile=logfile,
@@ -1880,6 +1874,7 @@ def get_password_from_file(password_file: str | None) -> str | None:
18801874
init_command=combined_init_cmd,
18811875
unbuffered=unbuffered,
18821876
charset=charset,
1877+
password_file=password_file,
18831878
)
18841879

18851880
if combined_init_cmd:

test/test_main.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111
from click.testing import CliRunner
1212
from pymysql.err import OperationalError
1313

14-
from mycli.main import MyCli, cli, is_valid_connection_scheme, thanks_picker
14+
from mycli.main import MyCli, cli, thanks_picker
15+
from mycli.packages.parseutils import is_valid_connection_scheme
1516
import mycli.packages.special
1617
from mycli.packages.special.main import COMMANDS as SPECIAL_COMMANDS
1718
from mycli.sqlexecute import ServerInfo, SQLExecute

0 commit comments

Comments
 (0)