1 | n/a | """Utilities to get a password and/or the current user name. |
---|
2 | n/a | |
---|
3 | n/a | getpass(prompt[, stream]) - Prompt for a password, with echo turned off. |
---|
4 | n/a | getuser() - Get the user name from the environment or password database. |
---|
5 | n/a | |
---|
6 | n/a | GetPassWarning - This UserWarning is issued when getpass() cannot prevent |
---|
7 | n/a | echoing of the password contents while reading. |
---|
8 | n/a | |
---|
9 | n/a | On Windows, the msvcrt module will be used. |
---|
10 | n/a | On the Mac EasyDialogs.AskPassword is used, if available. |
---|
11 | n/a | |
---|
12 | n/a | """ |
---|
13 | n/a | |
---|
14 | n/a | # Authors: Piers Lauder (original) |
---|
15 | n/a | # Guido van Rossum (Windows support and cleanup) |
---|
16 | n/a | # Gregory P. Smith (tty support & GetPassWarning) |
---|
17 | n/a | |
---|
18 | n/a | import contextlib |
---|
19 | n/a | import io |
---|
20 | n/a | import os |
---|
21 | n/a | import sys |
---|
22 | n/a | import warnings |
---|
23 | n/a | |
---|
24 | n/a | __all__ = ["getpass","getuser","GetPassWarning"] |
---|
25 | n/a | |
---|
26 | n/a | |
---|
27 | n/a | class GetPassWarning(UserWarning): pass |
---|
28 | n/a | |
---|
29 | n/a | |
---|
30 | n/a | def unix_getpass(prompt='Password: ', stream=None): |
---|
31 | n/a | """Prompt for a password, with echo turned off. |
---|
32 | n/a | |
---|
33 | n/a | Args: |
---|
34 | n/a | prompt: Written on stream to ask for the input. Default: 'Password: ' |
---|
35 | n/a | stream: A writable file object to display the prompt. Defaults to |
---|
36 | n/a | the tty. If no tty is available defaults to sys.stderr. |
---|
37 | n/a | Returns: |
---|
38 | n/a | The seKr3t input. |
---|
39 | n/a | Raises: |
---|
40 | n/a | EOFError: If our input tty or stdin was closed. |
---|
41 | n/a | GetPassWarning: When we were unable to turn echo off on the input. |
---|
42 | n/a | |
---|
43 | n/a | Always restores terminal settings before returning. |
---|
44 | n/a | """ |
---|
45 | n/a | passwd = None |
---|
46 | n/a | with contextlib.ExitStack() as stack: |
---|
47 | n/a | try: |
---|
48 | n/a | # Always try reading and writing directly on the tty first. |
---|
49 | n/a | fd = os.open('/dev/tty', os.O_RDWR|os.O_NOCTTY) |
---|
50 | n/a | tty = io.FileIO(fd, 'w+') |
---|
51 | n/a | stack.enter_context(tty) |
---|
52 | n/a | input = io.TextIOWrapper(tty) |
---|
53 | n/a | stack.enter_context(input) |
---|
54 | n/a | if not stream: |
---|
55 | n/a | stream = input |
---|
56 | n/a | except OSError as e: |
---|
57 | n/a | # If that fails, see if stdin can be controlled. |
---|
58 | n/a | stack.close() |
---|
59 | n/a | try: |
---|
60 | n/a | fd = sys.stdin.fileno() |
---|
61 | n/a | except (AttributeError, ValueError): |
---|
62 | n/a | fd = None |
---|
63 | n/a | passwd = fallback_getpass(prompt, stream) |
---|
64 | n/a | input = sys.stdin |
---|
65 | n/a | if not stream: |
---|
66 | n/a | stream = sys.stderr |
---|
67 | n/a | |
---|
68 | n/a | if fd is not None: |
---|
69 | n/a | try: |
---|
70 | n/a | old = termios.tcgetattr(fd) # a copy to save |
---|
71 | n/a | new = old[:] |
---|
72 | n/a | new[3] &= ~termios.ECHO # 3 == 'lflags' |
---|
73 | n/a | tcsetattr_flags = termios.TCSAFLUSH |
---|
74 | n/a | if hasattr(termios, 'TCSASOFT'): |
---|
75 | n/a | tcsetattr_flags |= termios.TCSASOFT |
---|
76 | n/a | try: |
---|
77 | n/a | termios.tcsetattr(fd, tcsetattr_flags, new) |
---|
78 | n/a | passwd = _raw_input(prompt, stream, input=input) |
---|
79 | n/a | finally: |
---|
80 | n/a | termios.tcsetattr(fd, tcsetattr_flags, old) |
---|
81 | n/a | stream.flush() # issue7208 |
---|
82 | n/a | except termios.error: |
---|
83 | n/a | if passwd is not None: |
---|
84 | n/a | # _raw_input succeeded. The final tcsetattr failed. Reraise |
---|
85 | n/a | # instead of leaving the terminal in an unknown state. |
---|
86 | n/a | raise |
---|
87 | n/a | # We can't control the tty or stdin. Give up and use normal IO. |
---|
88 | n/a | # fallback_getpass() raises an appropriate warning. |
---|
89 | n/a | if stream is not input: |
---|
90 | n/a | # clean up unused file objects before blocking |
---|
91 | n/a | stack.close() |
---|
92 | n/a | passwd = fallback_getpass(prompt, stream) |
---|
93 | n/a | |
---|
94 | n/a | stream.write('\n') |
---|
95 | n/a | return passwd |
---|
96 | n/a | |
---|
97 | n/a | |
---|
98 | n/a | def win_getpass(prompt='Password: ', stream=None): |
---|
99 | n/a | """Prompt for password with echo off, using Windows getch().""" |
---|
100 | n/a | if sys.stdin is not sys.__stdin__: |
---|
101 | n/a | return fallback_getpass(prompt, stream) |
---|
102 | n/a | |
---|
103 | n/a | for c in prompt: |
---|
104 | n/a | msvcrt.putwch(c) |
---|
105 | n/a | pw = "" |
---|
106 | n/a | while 1: |
---|
107 | n/a | c = msvcrt.getwch() |
---|
108 | n/a | if c == '\r' or c == '\n': |
---|
109 | n/a | break |
---|
110 | n/a | if c == '\003': |
---|
111 | n/a | raise KeyboardInterrupt |
---|
112 | n/a | if c == '\b': |
---|
113 | n/a | pw = pw[:-1] |
---|
114 | n/a | else: |
---|
115 | n/a | pw = pw + c |
---|
116 | n/a | msvcrt.putwch('\r') |
---|
117 | n/a | msvcrt.putwch('\n') |
---|
118 | n/a | return pw |
---|
119 | n/a | |
---|
120 | n/a | |
---|
121 | n/a | def fallback_getpass(prompt='Password: ', stream=None): |
---|
122 | n/a | warnings.warn("Can not control echo on the terminal.", GetPassWarning, |
---|
123 | n/a | stacklevel=2) |
---|
124 | n/a | if not stream: |
---|
125 | n/a | stream = sys.stderr |
---|
126 | n/a | print("Warning: Password input may be echoed.", file=stream) |
---|
127 | n/a | return _raw_input(prompt, stream) |
---|
128 | n/a | |
---|
129 | n/a | |
---|
130 | n/a | def _raw_input(prompt="", stream=None, input=None): |
---|
131 | n/a | # This doesn't save the string in the GNU readline history. |
---|
132 | n/a | if not stream: |
---|
133 | n/a | stream = sys.stderr |
---|
134 | n/a | if not input: |
---|
135 | n/a | input = sys.stdin |
---|
136 | n/a | prompt = str(prompt) |
---|
137 | n/a | if prompt: |
---|
138 | n/a | try: |
---|
139 | n/a | stream.write(prompt) |
---|
140 | n/a | except UnicodeEncodeError: |
---|
141 | n/a | # Use replace error handler to get as much as possible printed. |
---|
142 | n/a | prompt = prompt.encode(stream.encoding, 'replace') |
---|
143 | n/a | prompt = prompt.decode(stream.encoding) |
---|
144 | n/a | stream.write(prompt) |
---|
145 | n/a | stream.flush() |
---|
146 | n/a | # NOTE: The Python C API calls flockfile() (and unlock) during readline. |
---|
147 | n/a | line = input.readline() |
---|
148 | n/a | if not line: |
---|
149 | n/a | raise EOFError |
---|
150 | n/a | if line[-1] == '\n': |
---|
151 | n/a | line = line[:-1] |
---|
152 | n/a | return line |
---|
153 | n/a | |
---|
154 | n/a | |
---|
155 | n/a | def getuser(): |
---|
156 | n/a | """Get the username from the environment or password database. |
---|
157 | n/a | |
---|
158 | n/a | First try various environment variables, then the password |
---|
159 | n/a | database. This works on Windows as long as USERNAME is set. |
---|
160 | n/a | |
---|
161 | n/a | """ |
---|
162 | n/a | |
---|
163 | n/a | for name in ('LOGNAME', 'USER', 'LNAME', 'USERNAME'): |
---|
164 | n/a | user = os.environ.get(name) |
---|
165 | n/a | if user: |
---|
166 | n/a | return user |
---|
167 | n/a | |
---|
168 | n/a | # If this fails, the exception will "explain" why |
---|
169 | n/a | import pwd |
---|
170 | n/a | return pwd.getpwuid(os.getuid())[0] |
---|
171 | n/a | |
---|
172 | n/a | # Bind the name getpass to the appropriate function |
---|
173 | n/a | try: |
---|
174 | n/a | import termios |
---|
175 | n/a | # it's possible there is an incompatible termios from the |
---|
176 | n/a | # McMillan Installer, make sure we have a UNIX-compatible termios |
---|
177 | n/a | termios.tcgetattr, termios.tcsetattr |
---|
178 | n/a | except (ImportError, AttributeError): |
---|
179 | n/a | try: |
---|
180 | n/a | import msvcrt |
---|
181 | n/a | except ImportError: |
---|
182 | n/a | getpass = fallback_getpass |
---|
183 | n/a | else: |
---|
184 | n/a | getpass = win_getpass |
---|
185 | n/a | else: |
---|
186 | n/a | getpass = unix_getpass |
---|