Merge ~jugmac00/lazr.sshserver:apply-black into lazr.sshserver:main
- Git
- lp:~jugmac00/lazr.sshserver
- apply-black
- Merge into main
Proposed by
Jürgen Gmach
Status: | Merged |
---|---|
Merged at revision: | 316d2b283f1fb1238d36d7d7d225b7f89b10e6a4 |
Proposed branch: | ~jugmac00/lazr.sshserver:apply-black |
Merge into: | lazr.sshserver:main |
Diff against target: |
1965 lines (+534/-410) 18 files modified
.git-blame-ignore-revs (+2/-0) .pre-commit-config.yaml (+15/-2) NEWS.txt (+2/-0) pyproject.toml (+8/-0) setup.py (+34/-34) src/lazr/__init__.py (+2/-0) src/lazr/sshserver/accesslog.py (+5/-8) src/lazr/sshserver/auth.py (+75/-64) src/lazr/sshserver/events.py (+26/-30) src/lazr/sshserver/service.py (+39/-28) src/lazr/sshserver/session.py (+12/-16) src/lazr/sshserver/sftp.py (+5/-4) src/lazr/sshserver/tests/test_accesslog.py (+20/-20) src/lazr/sshserver/tests/test_auth.py (+216/-154) src/lazr/sshserver/tests/test_docs.py (+23/-14) src/lazr/sshserver/tests/test_events.py (+14/-15) src/lazr/sshserver/tests/test_session.py (+35/-20) tox.ini (+1/-1) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Colin Watson (community) | Approve | ||
Review via email: mp+411678@code.launchpad.net |
Commit message
Apply black code formatter
Description of the change
To post a comment you must log in.
Revision history for this message
Colin Watson (cjwatson) : | # |
review:
Approve
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs |
2 | new file mode 100644 |
3 | index 0000000..3a635e3 |
4 | --- /dev/null |
5 | +++ b/.git-blame-ignore-revs |
6 | @@ -0,0 +1,2 @@ |
7 | +# apply black |
8 | +a08ff20b65010305eaf0a4de60c7913146658a76 |
9 | diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml |
10 | index d62393a..d4436ec 100644 |
11 | --- a/.pre-commit-config.yaml |
12 | +++ b/.pre-commit-config.yaml |
13 | @@ -2,14 +2,27 @@ |
14 | # See https://pre-commit.com/hooks.html for more hooks |
15 | repos: |
16 | - repo: https://github.com/pre-commit/pre-commit-hooks |
17 | - rev: v3.2.0 |
18 | + rev: v4.0.1 |
19 | hooks: |
20 | - id: check-added-large-files |
21 | + - id: check-ast |
22 | + - id: check-json |
23 | - id: check-merge-conflict |
24 | + - id: check-toml |
25 | - id: check-xml |
26 | - id: check-yaml |
27 | - id: debug-statements |
28 | + - id: end-of-file-fixer |
29 | + - id: trailing-whitespace |
30 | +- repo: https://github.com/psf/black |
31 | + rev: 21.10b0 |
32 | + hooks: |
33 | + - id: black |
34 | - repo: https://github.com/PyCQA/flake8 |
35 | - rev: 3.9.2 |
36 | + rev: 4.0.1 |
37 | hooks: |
38 | - id: flake8 |
39 | +- repo: https://github.com/PyCQA/isort |
40 | + rev: 5.10.1 |
41 | + hooks: |
42 | + - id: isort |
43 | diff --git a/NEWS.txt b/NEWS.txt |
44 | index 2981bee..43c2138 100644 |
45 | --- a/NEWS.txt |
46 | +++ b/NEWS.txt |
47 | @@ -7,6 +7,8 @@ NEWS for lazr.sshserver |
48 | |
49 | - Officially add support for Python 3.9 and 3.10. |
50 | - Add basic pre-commit configuration. |
51 | +- Apply black code formatter. |
52 | +- Add isort pre-commit hook. |
53 | |
54 | 0.1.12 (2021-09-13) |
55 | =================== |
56 | diff --git a/pyproject.toml b/pyproject.toml |
57 | new file mode 100644 |
58 | index 0000000..15e7219 |
59 | --- /dev/null |
60 | +++ b/pyproject.toml |
61 | @@ -0,0 +1,8 @@ |
62 | +[tool.black] |
63 | +line-length = 79 |
64 | +target-version = ['py27'] |
65 | + |
66 | +[tool.isort] |
67 | +profile = "black" |
68 | +multi_line_output = 3 |
69 | +line_length = 79 |
70 | diff --git a/setup.py b/setup.py |
71 | index 2d54c2e..ce0f879 100755 |
72 | --- a/setup.py |
73 | +++ b/setup.py |
74 | @@ -16,52 +16,52 @@ |
75 | # You should have received a copy of the GNU Lesser General Public License |
76 | # along with lazr.sshserver. If not, see <http://www.gnu.org/licenses/>. |
77 | |
78 | -from setuptools import setup, find_packages |
79 | +from setuptools import find_packages, setup |
80 | |
81 | |
82 | # generic helpers primarily for the long_description |
83 | def generate(*docname_or_string): |
84 | res = [] |
85 | for value in docname_or_string: |
86 | - if value.endswith('.txt'): |
87 | + if value.endswith(".txt"): |
88 | f = open(value) |
89 | value = f.read() |
90 | f.close() |
91 | res.append(value) |
92 | - if not value.endswith('\n'): |
93 | - res.append('') |
94 | - return '\n'.join(res) |
95 | + if not value.endswith("\n"): |
96 | + res.append("") |
97 | + return "\n".join(res) |
98 | + |
99 | + |
100 | # end generic helpers |
101 | |
102 | |
103 | setup( |
104 | - name='lazr.sshserver', |
105 | - version='0.1.12', |
106 | - namespace_packages=['lazr'], |
107 | - packages=find_packages('src'), |
108 | - package_dir={'': 'src'}, |
109 | + name="lazr.sshserver", |
110 | + version="0.1.12", |
111 | + namespace_packages=["lazr"], |
112 | + packages=find_packages("src"), |
113 | + package_dir={"": "src"}, |
114 | include_package_data=True, |
115 | zip_safe=False, |
116 | - maintainer='LAZR Developers', |
117 | - maintainer_email='lazr-developers@lists.launchpad.net', |
118 | - description=open('README.txt').readline().strip(), |
119 | - long_description=generate( |
120 | - 'src/lazr/sshserver/README.txt', |
121 | - 'NEWS.txt'), |
122 | - license='LGPL v3', |
123 | + maintainer="LAZR Developers", |
124 | + maintainer_email="lazr-developers@lists.launchpad.net", |
125 | + description=open("README.txt").readline().strip(), |
126 | + long_description=generate("src/lazr/sshserver/README.txt", "NEWS.txt"), |
127 | + license="LGPL v3", |
128 | install_requires=[ |
129 | - 'setuptools', |
130 | - 'Twisted[conch]>=16.2.0', |
131 | - 'zope.component', |
132 | - 'zope.event', |
133 | - 'zope.interface', |
134 | - ], |
135 | - url='https://launchpad.net/lazr.sshserver', |
136 | + "setuptools", |
137 | + "Twisted[conch]>=16.2.0", |
138 | + "zope.component", |
139 | + "zope.event", |
140 | + "zope.interface", |
141 | + ], |
142 | + url="https://launchpad.net/lazr.sshserver", |
143 | project_urls={ |
144 | "Source": "https://code.launchpad.net/lazr.sshserver", |
145 | "Issue Tracker": "https://bugs.launchpad.net/lazr.sshserver", |
146 | }, |
147 | - download_url='https://launchpad.net/lazr.sshserver/+download', |
148 | + download_url="https://launchpad.net/lazr.sshserver/+download", |
149 | classifiers=[ |
150 | "Development Status :: 5 - Production/Stable", |
151 | "Intended Audience :: Developers", |
152 | @@ -77,16 +77,16 @@ setup( |
153 | "Programming Language :: Python :: 3.8", |
154 | "Programming Language :: Python :: 3.9", |
155 | "Programming Language :: Python :: 3.10", |
156 | - ], |
157 | + ], |
158 | extras_require=dict( |
159 | - docs=['Sphinx'], |
160 | + docs=["Sphinx"], |
161 | test=[ |
162 | 'bzr; python_version < "3.0"', |
163 | - 'fixtures', |
164 | - 'flake8', |
165 | - 'testtools', |
166 | - 'zope.testrunner', |
167 | - ], |
168 | + "fixtures", |
169 | + "flake8", |
170 | + "testtools", |
171 | + "zope.testrunner", |
172 | + ], |
173 | ), |
174 | - test_suite='lazr.sshserver.tests', |
175 | - ) |
176 | + test_suite="lazr.sshserver.tests", |
177 | +) |
178 | diff --git a/src/lazr/__init__.py b/src/lazr/__init__.py |
179 | index ee457d8..1b88851 100644 |
180 | --- a/src/lazr/__init__.py |
181 | +++ b/src/lazr/__init__.py |
182 | @@ -17,7 +17,9 @@ |
183 | # this is a namespace package |
184 | try: |
185 | import pkg_resources |
186 | + |
187 | pkg_resources.declare_namespace(__name__) |
188 | except ImportError: |
189 | import pkgutil |
190 | + |
191 | __path__ = pkgutil.extend_path(__path__, __name__) |
192 | diff --git a/src/lazr/sshserver/accesslog.py b/src/lazr/sshserver/accesslog.py |
193 | index 7471d7d..c18c7a9 100644 |
194 | --- a/src/lazr/sshserver/accesslog.py |
195 | +++ b/src/lazr/sshserver/accesslog.py |
196 | @@ -7,19 +7,15 @@ from __future__ import absolute_import, print_function |
197 | |
198 | __metaclass__ = type |
199 | __all__ = [ |
200 | - 'LoggingManager', |
201 | - ] |
202 | + "LoggingManager", |
203 | +] |
204 | |
205 | import logging |
206 | from logging.handlers import WatchedFileHandler |
207 | |
208 | -from zope.component import ( |
209 | - adapter, |
210 | - getGlobalSiteManager, |
211 | - provideHandler, |
212 | - ) |
213 | # This non-standard import is necessary to hook up the event system. |
214 | import zope.component.event # noqa: F401 |
215 | +from zope.component import adapter, getGlobalSiteManager, provideHandler |
216 | |
217 | from lazr.sshserver.events import ILoggingEvent |
218 | |
219 | @@ -55,7 +51,8 @@ class LoggingManager: |
220 | log.addHandler(self._main_handler) |
221 | self._access_handler = WatchedFileHandler(self._access_log_path) |
222 | self._access_handler.setFormatter( |
223 | - logging.Formatter("%(asctime)s %(levelname)s %(message)s")) |
224 | + logging.Formatter("%(asctime)s %(levelname)s %(message)s") |
225 | + ) |
226 | self._access_log.addHandler(self._access_handler) |
227 | self._access_log.setLevel(logging.INFO) |
228 | # Make sure that our logging event handler is there, ready to receive |
229 | diff --git a/src/lazr/sshserver/auth.py b/src/lazr/sshserver/auth.py |
230 | index f5510e9..3d88930 100644 |
231 | --- a/src/lazr/sshserver/auth.py |
232 | +++ b/src/lazr/sshserver/auth.py |
233 | @@ -13,29 +13,20 @@ from __future__ import absolute_import, print_function |
234 | |
235 | __metaclass__ = type |
236 | __all__ = [ |
237 | - 'LaunchpadAvatar', |
238 | - 'PublicKeyFromLaunchpadChecker', |
239 | - 'SSHUserAuthServer', |
240 | - ] |
241 | + "LaunchpadAvatar", |
242 | + "PublicKeyFromLaunchpadChecker", |
243 | + "SSHUserAuthServer", |
244 | +] |
245 | |
246 | import base64 |
247 | import binascii |
248 | import sys |
249 | |
250 | from twisted.conch import avatar |
251 | -from twisted.conch.error import ( |
252 | - ConchError, |
253 | - ValidPublicKey, |
254 | - ) |
255 | +from twisted.conch.error import ConchError, ValidPublicKey |
256 | from twisted.conch.interfaces import IConchUser |
257 | -from twisted.conch.ssh import ( |
258 | - keys, |
259 | - userauth, |
260 | - ) |
261 | -from twisted.conch.ssh.common import ( |
262 | - getNS, |
263 | - NS, |
264 | - ) |
265 | +from twisted.conch.ssh import keys, userauth |
266 | +from twisted.conch.ssh.common import NS, getNS |
267 | from twisted.cred import credentials |
268 | from twisted.cred.checkers import ICredentialsChecker |
269 | from twisted.cred.error import UnauthorizedLogin |
270 | @@ -50,7 +41,6 @@ from lazr.sshserver import events |
271 | from lazr.sshserver.session import PatchedSSHSession |
272 | from lazr.sshserver.sftp import FileTransferServer |
273 | |
274 | - |
275 | log = Logger() |
276 | |
277 | |
278 | @@ -77,11 +67,12 @@ class NoSuchPersonWithName(xmlrpc.Fault): |
279 | """There's no Person with the specified name registered in Launchpad.""" |
280 | |
281 | error_code = 200 |
282 | - msg_template = 'No such person or team: %(person_name)s' |
283 | + msg_template = "No such person or team: %(person_name)s" |
284 | |
285 | def __init__(self, person_name): |
286 | super(NoSuchPersonWithName, self).__init__( |
287 | - self.error_code, self.msg_template % {"person_name": person_name}) |
288 | + self.error_code, self.msg_template % {"person_name": person_name} |
289 | + ) |
290 | |
291 | |
292 | class LaunchpadAvatar(avatar.ConchUser): |
293 | @@ -100,14 +91,14 @@ class LaunchpadAvatar(avatar.ConchUser): |
294 | `IAuthServer.getUserAndSSHKeys`. |
295 | """ |
296 | avatar.ConchUser.__init__(self) |
297 | - self.user_id = user_dict['id'] |
298 | - self.username = user_dict['name'] |
299 | + self.user_id = user_dict["id"] |
300 | + self.username = user_dict["name"] |
301 | |
302 | # Set the only channel as a standard SSH session (with a couple of bug |
303 | # fixes). |
304 | - self.channelLookup = {b'session': PatchedSSHSession} |
305 | + self.channelLookup = {b"session": PatchedSSHSession} |
306 | # ...and set the only subsystem to be SFTP. |
307 | - self.subsystemLookup = {b'sftp': FileTransferServer} |
308 | + self.subsystemLookup = {b"sftp": FileTransferServer} |
309 | |
310 | def logout(self): |
311 | notify(events.UserLoggedOut(self)) |
312 | @@ -127,7 +118,8 @@ class SSHPrivateKeyWithMind(credentials.SSHPrivateKey): |
313 | |
314 | def __init__(self, username, algName, blob, sigData, signature, mind): |
315 | credentials.SSHPrivateKey.__init__( |
316 | - self, username, algName, blob, sigData, signature) |
317 | + self, username, algName, blob, sigData, signature |
318 | + ) |
319 | self.mind = mind |
320 | |
321 | |
322 | @@ -154,11 +146,11 @@ class UserDetailsMind: |
323 | endpoint. |
324 | :param username: The username to look up. |
325 | """ |
326 | - username = username.decode('UTF-8') |
327 | + username = username.decode("UTF-8") |
328 | if username in self.cache: |
329 | return defer.succeed(self.cache[username]) |
330 | else: |
331 | - d = proxy.callRemote('getUserAndSSHKeys', username) |
332 | + d = proxy.callRemote("getUserAndSSHKeys", username) |
333 | d.addBoth(self._add_to_cache, username) |
334 | return d |
335 | |
336 | @@ -189,12 +181,13 @@ class SSHUserAuthServer(userauth.SSHUserAuthServer): |
337 | self._configured_banner_sent = False |
338 | self._mind = UserDetailsMind() |
339 | self.interfaceToMethod = userauth.SSHUserAuthServer.interfaceToMethod |
340 | - self.interfaceToMethod[ISSHPrivateKeyWithMind] = b'publickey' |
341 | + self.interfaceToMethod[ISSHPrivateKeyWithMind] = b"publickey" |
342 | |
343 | - def sendBanner(self, text, language='en'): |
344 | - bytes = b'\r\n'.join(text.encode('UTF8').splitlines() + [b'']) |
345 | - self.transport.sendPacket(userauth.MSG_USERAUTH_BANNER, |
346 | - NS(bytes) + NS(language)) |
347 | + def sendBanner(self, text, language="en"): |
348 | + bytes = b"\r\n".join(text.encode("UTF8").splitlines() + [b""]) |
349 | + self.transport.sendPacket( |
350 | + userauth.MSG_USERAUTH_BANNER, NS(bytes) + NS(language) |
351 | + ) |
352 | |
353 | def _sendConfiguredBanner(self, passed_through): |
354 | if not self._configured_banner_sent and self._banner: |
355 | @@ -214,7 +207,7 @@ class SSHUserAuthServer(userauth.SSHUserAuthServer): |
356 | self.method = method |
357 | d = self.tryAuth(method, user, rest) |
358 | if not d: |
359 | - self._ebBadAuth(failure.Failure(ConchError('auth returned none'))) |
360 | + self._ebBadAuth(failure.Failure(ConchError("auth returned none"))) |
361 | return |
362 | d.addCallback(self._sendConfiguredBanner) |
363 | d.addCallback(self._cbFinishedAuth) |
364 | @@ -246,8 +239,9 @@ class SSHUserAuthServer(userauth.SSHUserAuthServer): |
365 | """ |
366 | return self._mind |
367 | |
368 | - def makePublicKeyCredentials(self, username, algName, blob, sigData, |
369 | - signature): |
370 | + def makePublicKeyCredentials( |
371 | + self, username, algName, blob, sigData, signature |
372 | + ): |
373 | """Construct credentials for a request to login with a public key. |
374 | |
375 | Our implementation returns a SSHPrivateKeyWithMind. |
376 | @@ -261,7 +255,8 @@ class SSHUserAuthServer(userauth.SSHUserAuthServer): |
377 | """ |
378 | mind = self.getMind() |
379 | return SSHPrivateKeyWithMind( |
380 | - username, algName, blob, sigData, signature, mind) |
381 | + username, algName, blob, sigData, signature, mind |
382 | + ) |
383 | |
384 | def auth_publickey(self, packet): |
385 | # This is copied and pasted from twisted/conch/ssh/userauth.py in |
386 | @@ -278,30 +273,38 @@ class SSHUserAuthServer(userauth.SSHUserAuthServer): |
387 | # Work around a bug in paramiko < 2.0.0: if the most significant |
388 | # byte of an RSA signature is zero, then it strips leading zero |
389 | # bytes rather than zero-padding it to the correct length. |
390 | - if algName == b'ssh-rsa': |
391 | + if algName == b"ssh-rsa": |
392 | signatureType, rawSignature, rest = getNS(signature, 2) |
393 | pubKeyLen = (pubKey.size() + 7) // 8 |
394 | if len(rawSignature) < pubKeyLen: |
395 | rawSignature = ( |
396 | - b'\x00' * (pubKeyLen - len(rawSignature)) + |
397 | - rawSignature) |
398 | + b"\x00" * (pubKeyLen - len(rawSignature)) |
399 | + + rawSignature |
400 | + ) |
401 | signature = NS(signatureType) + NS(rawSignature) + rest |
402 | b = ( |
403 | - NS(self.transport.sessionID) + |
404 | - _bytesChr(userauth.MSG_USERAUTH_REQUEST) + NS(self.user) + |
405 | - NS(self.nextService) + NS(b'publickey') + |
406 | - _bytesChr(hasSig) + NS(pubKey.sshType()) + NS(blob)) |
407 | + NS(self.transport.sessionID) |
408 | + + _bytesChr(userauth.MSG_USERAUTH_REQUEST) |
409 | + + NS(self.user) |
410 | + + NS(self.nextService) |
411 | + + NS(b"publickey") |
412 | + + _bytesChr(hasSig) |
413 | + + NS(pubKey.sshType()) |
414 | + + NS(blob) |
415 | + ) |
416 | # The next three lines are different from the original. |
417 | c = self.makePublicKeyCredentials( |
418 | - self.user, algName, blob, b, signature) |
419 | + self.user, algName, blob, b, signature |
420 | + ) |
421 | return self.portal.login(c, self.getMind(), IConchUser) |
422 | else: |
423 | # The next four lines are different from the original. |
424 | c = self.makePublicKeyCredentials( |
425 | - self.user, algName, blob, None, None) |
426 | - return self.portal.login( |
427 | - c, self.getMind(), IConchUser).addErrback( |
428 | - self._ebCheckKey, packet[1:]) |
429 | + self.user, algName, blob, None, None |
430 | + ) |
431 | + return self.portal.login(c, self.getMind(), IConchUser).addErrback( |
432 | + self._ebCheckKey, packet[1:] |
433 | + ) |
434 | |
435 | |
436 | # XXX cjwatson 2019-10-18: Ideally we'd use |
437 | @@ -315,6 +318,7 @@ class PublicKeyFromLaunchpadChecker: |
438 | |
439 | It knows how to get the public keys from the authserver. |
440 | """ |
441 | + |
442 | credentialInterfaces = (ISSHPrivateKeyWithMind,) |
443 | |
444 | def __init__(self, authserver): |
445 | @@ -333,13 +337,17 @@ class PublicKeyFromLaunchpadChecker: |
446 | has registered in Launchpad. |
447 | """ |
448 | try: |
449 | - username = credentials.username.decode('UTF-8') |
450 | + username = credentials.username.decode("UTF-8") |
451 | except UnicodeDecodeError: |
452 | # Launchpad account names must be valid UTF-8. |
453 | - return defer.fail(UserDisplayedUnauthorizedLogin( |
454 | - "No such Launchpad account: %r" % credentials.username)) |
455 | + return defer.fail( |
456 | + UserDisplayedUnauthorizedLogin( |
457 | + "No such Launchpad account: %r" % credentials.username |
458 | + ) |
459 | + ) |
460 | d = credentials.mind.lookupUserDetails( |
461 | - self.authserver, credentials.username) |
462 | + self.authserver, credentials.username |
463 | + ) |
464 | d.addCallback(self._checkForAuthorizedKey, credentials) |
465 | d.addErrback(self._reportNoSuchUser, username) |
466 | return d |
467 | @@ -350,29 +358,31 @@ class PublicKeyFromLaunchpadChecker: |
468 | fault = failure.value |
469 | if fault.faultCode == NoSuchPersonWithName.error_code: |
470 | raise UserDisplayedUnauthorizedLogin( |
471 | - "No such Launchpad account: %s" % username) |
472 | + "No such Launchpad account: %s" % username |
473 | + ) |
474 | raise failure |
475 | |
476 | def _checkForAuthorizedKey(self, user_dict, credentials): |
477 | """Check the key data in credentials against the keys found in LP.""" |
478 | - if credentials.algName == b'ssh-dss': |
479 | - wantKeyType = 'DSA' |
480 | - elif credentials.algName == b'ssh-rsa': |
481 | - wantKeyType = 'RSA' |
482 | - elif credentials.algName.startswith(b'ecdsa-sha2-'): |
483 | - wantKeyType = 'ECDSA' |
484 | - elif credentials.algName == b'ssh-ed25519': |
485 | - wantKeyType = 'ED25519' |
486 | + if credentials.algName == b"ssh-dss": |
487 | + wantKeyType = "DSA" |
488 | + elif credentials.algName == b"ssh-rsa": |
489 | + wantKeyType = "RSA" |
490 | + elif credentials.algName.startswith(b"ecdsa-sha2-"): |
491 | + wantKeyType = "ECDSA" |
492 | + elif credentials.algName == b"ssh-ed25519": |
493 | + wantKeyType = "ED25519" |
494 | else: |
495 | # unknown key type |
496 | return False |
497 | |
498 | - if len(user_dict['keys']) == 0: |
499 | + if len(user_dict["keys"]) == 0: |
500 | raise UserDisplayedUnauthorizedLogin( |
501 | "Launchpad user '%s' doesn't have a registered SSH key" |
502 | - % user_dict['name']) |
503 | + % user_dict["name"] |
504 | + ) |
505 | |
506 | - for keytype, keytext in user_dict['keys']: |
507 | + for keytype, keytext in user_dict["keys"]: |
508 | if keytype != wantKeyType: |
509 | continue |
510 | try: |
511 | @@ -383,7 +393,8 @@ class PublicKeyFromLaunchpadChecker: |
512 | |
513 | raise UnauthorizedLogin( |
514 | "Your SSH key does not match any key registered for Launchpad " |
515 | - "user %s" % user_dict['name']) |
516 | + "user %s" % user_dict["name"] |
517 | + ) |
518 | |
519 | def _verifyKey(self, validKey, credentials): |
520 | """Check whether the credentials themselves are valid. |
521 | diff --git a/src/lazr/sshserver/events.py b/src/lazr/sshserver/events.py |
522 | index ccc59be..5d0165b 100644 |
523 | --- a/src/lazr/sshserver/events.py |
524 | +++ b/src/lazr/sshserver/events.py |
525 | @@ -7,27 +7,23 @@ from __future__ import absolute_import, print_function |
526 | |
527 | __metaclass__ = type |
528 | __all__ = [ |
529 | - 'AuthenticationFailed', |
530 | - 'AvatarEvent', |
531 | - 'ILoggingEvent', |
532 | - 'LoggingEvent', |
533 | - 'ServerStarting', |
534 | - 'ServerStopped', |
535 | - 'SFTPClosed', |
536 | - 'SFTPStarted', |
537 | - 'UserConnected', |
538 | - 'UserDisconnected', |
539 | - 'UserLoggedIn', |
540 | - 'UserLoggedOut', |
541 | - ] |
542 | + "AuthenticationFailed", |
543 | + "AvatarEvent", |
544 | + "ILoggingEvent", |
545 | + "LoggingEvent", |
546 | + "ServerStarting", |
547 | + "ServerStopped", |
548 | + "SFTPClosed", |
549 | + "SFTPStarted", |
550 | + "UserConnected", |
551 | + "UserDisconnected", |
552 | + "UserLoggedIn", |
553 | + "UserLoggedOut", |
554 | +] |
555 | |
556 | import logging |
557 | |
558 | -from zope.interface import ( |
559 | - Attribute, |
560 | - implementer, |
561 | - Interface, |
562 | - ) |
563 | +from zope.interface import Attribute, Interface, implementer |
564 | |
565 | |
566 | class ILoggingEvent(Interface): |
567 | @@ -81,29 +77,28 @@ class LoggingEvent: |
568 | class ServerStarting(LoggingEvent): |
569 | |
570 | level = logging.INFO |
571 | - template = '---- Server started ----' |
572 | + template = "---- Server started ----" |
573 | |
574 | |
575 | class ServerStopped(LoggingEvent): |
576 | |
577 | level = logging.INFO |
578 | - template = '---- Server stopped ----' |
579 | + template = "---- Server stopped ----" |
580 | |
581 | |
582 | class UserConnected(LoggingEvent): |
583 | |
584 | level = logging.INFO |
585 | - template = '[%(session_id)s] %(address)s connected.' |
586 | + template = "[%(session_id)s] %(address)s connected." |
587 | |
588 | def __init__(self, transport, address): |
589 | - LoggingEvent.__init__( |
590 | - self, session_id=id(transport), address=address) |
591 | + LoggingEvent.__init__(self, session_id=id(transport), address=address) |
592 | |
593 | |
594 | class AuthenticationFailed(LoggingEvent): |
595 | |
596 | level = logging.INFO |
597 | - template = '[%(session_id)s] failed to authenticate.' |
598 | + template = "[%(session_id)s] failed to authenticate." |
599 | |
600 | def __init__(self, transport): |
601 | LoggingEvent.__init__(self, session_id=id(transport)) |
602 | @@ -112,7 +107,7 @@ class AuthenticationFailed(LoggingEvent): |
603 | class UserDisconnected(LoggingEvent): |
604 | |
605 | level = logging.INFO |
606 | - template = '[%(session_id)s] disconnected.' |
607 | + template = "[%(session_id)s] disconnected." |
608 | |
609 | def __init__(self, transport): |
610 | LoggingEvent.__init__(self, session_id=id(transport)) |
611 | @@ -126,24 +121,25 @@ class AvatarEvent(LoggingEvent): |
612 | def __init__(self, avatar): |
613 | self.avatar = avatar |
614 | LoggingEvent.__init__( |
615 | - self, session_id=id(avatar.transport), username=avatar.username) |
616 | + self, session_id=id(avatar.transport), username=avatar.username |
617 | + ) |
618 | |
619 | |
620 | class UserLoggedIn(AvatarEvent): |
621 | |
622 | - template = '[%(session_id)s] %(username)s logged in.' |
623 | + template = "[%(session_id)s] %(username)s logged in." |
624 | |
625 | |
626 | class UserLoggedOut(AvatarEvent): |
627 | |
628 | - template = '[%(session_id)s] %(username)s disconnected.' |
629 | + template = "[%(session_id)s] %(username)s disconnected." |
630 | |
631 | |
632 | class SFTPStarted(AvatarEvent): |
633 | |
634 | - template = '[%(session_id)s] %(username)s started SFTP session.' |
635 | + template = "[%(session_id)s] %(username)s started SFTP session." |
636 | |
637 | |
638 | class SFTPClosed(AvatarEvent): |
639 | |
640 | - template = '[%(session_id)s] %(username)s closed SFTP session.' |
641 | + template = "[%(session_id)s] %(username)s closed SFTP session." |
642 | diff --git a/src/lazr/sshserver/service.py b/src/lazr/sshserver/service.py |
643 | index 023d866..2d0622f 100644 |
644 | --- a/src/lazr/sshserver/service.py |
645 | +++ b/src/lazr/sshserver/service.py |
646 | @@ -10,17 +10,14 @@ from __future__ import absolute_import, print_function |
647 | |
648 | __metaclass__ = type |
649 | __all__ = [ |
650 | - 'SSHService', |
651 | - ] |
652 | + "SSHService", |
653 | +] |
654 | |
655 | |
656 | import logging |
657 | import os |
658 | |
659 | -from twisted.application import ( |
660 | - service, |
661 | - strports, |
662 | - ) |
663 | +from twisted.application import service, strports |
664 | from twisted.conch.openssh_compat import primes |
665 | from twisted.conch.ssh.factory import SSHFactory |
666 | from twisted.conch.ssh.keys import Key |
667 | @@ -28,10 +25,7 @@ from twisted.conch.ssh.transport import SSHServerTransport |
668 | from twisted.internet import defer |
669 | from zope.event import notify |
670 | |
671 | -from lazr.sshserver import ( |
672 | - accesslog, |
673 | - events, |
674 | - ) |
675 | +from lazr.sshserver import accesslog, events |
676 | from lazr.sshserver.auth import SSHUserAuthServer |
677 | |
678 | |
679 | @@ -48,6 +42,7 @@ def gatherResults(deferredList): |
680 | :type deferredList: list of `defer.Deferred`s. |
681 | :return: `defer.Deferred`. |
682 | """ |
683 | + |
684 | def convert_first_error_to_real(failure): |
685 | failure.trap(defer.FirstError) |
686 | return failure.value.subFailure |
687 | @@ -59,7 +54,6 @@ def gatherResults(deferredList): |
688 | |
689 | |
690 | class KeepAliveSettingSSHServerTransport(SSHServerTransport): |
691 | - |
692 | def connectionMade(self): |
693 | SSHServerTransport.connectionMade(self) |
694 | self.transport.setTcpKeepAlive(True) |
695 | @@ -75,8 +69,9 @@ class Factory(SSHFactory): |
696 | |
697 | protocol = KeepAliveSettingSSHServerTransport |
698 | |
699 | - def __init__(self, portal, private_key, public_key, banner=None, |
700 | - moduli_path=None): |
701 | + def __init__( |
702 | + self, portal, private_key, public_key, banner=None, moduli_path=None |
703 | + ): |
704 | """Construct an SSH factory. |
705 | |
706 | :param portal: The portal used to turn credentials into users. |
707 | @@ -92,14 +87,14 @@ class Factory(SSHFactory): |
708 | # at it. (Look for the beautiful line "self.portal = |
709 | # self.transport.factory.portal"). |
710 | self.portal = portal |
711 | - self.services[b'ssh-userauth'] = self._makeAuthServer |
712 | + self.services[b"ssh-userauth"] = self._makeAuthServer |
713 | self._private_key = private_key |
714 | self._public_key = public_key |
715 | self._banner = banner |
716 | self._moduli_path = moduli_path |
717 | |
718 | def _makeAuthServer(self, *args, **kwargs): |
719 | - kwargs['banner'] = self._banner |
720 | + kwargs["banner"] = self._banner |
721 | return SSHUserAuthServer(*args, **kwargs) |
722 | |
723 | def buildProtocol(self, address): |
724 | @@ -110,8 +105,9 @@ class Factory(SSHFactory): |
725 | """ |
726 | transport = SSHFactory.buildProtocol(self, address) |
727 | transport._realConnectionLost = transport.connectionLost |
728 | - transport.connectionLost = ( |
729 | - lambda reason: self.connectionLost(transport, reason)) |
730 | + transport.connectionLost = lambda reason: self.connectionLost( |
731 | + transport, reason |
732 | + ) |
733 | notify(events.UserConnected(transport, address)) |
734 | return transport |
735 | |
736 | @@ -130,7 +126,7 @@ class Factory(SSHFactory): |
737 | # |
738 | # b) the server doesn't normally generate a "go away" event. |
739 | # Rather, the client simply stops trying. |
740 | - if getattr(transport, 'avatar', None) is None: |
741 | + if getattr(transport, "avatar", None) is None: |
742 | notify(events.AuthenticationFailed(transport)) |
743 | notify(events.UserDisconnected(transport)) |
744 | |
745 | @@ -139,14 +135,14 @@ class Factory(SSHFactory): |
746 | |
747 | See `SSHFactory.getPublicKeys`. |
748 | """ |
749 | - return {b'ssh-rsa': self._public_key} |
750 | + return {b"ssh-rsa": self._public_key} |
751 | |
752 | def getPrivateKeys(self): |
753 | """Return the server's configured private key. |
754 | |
755 | See `SSHFactory.getPrivateKeys`. |
756 | """ |
757 | - return {b'ssh-rsa': self._private_key} |
758 | + return {b"ssh-rsa": self._private_key} |
759 | |
760 | def getPrimes(self): |
761 | try: |
762 | @@ -160,9 +156,19 @@ class Factory(SSHFactory): |
763 | class SSHService(service.Service): |
764 | """A Twisted service for the SSH server.""" |
765 | |
766 | - def __init__(self, portal, private_key_path, public_key_path, |
767 | - main_log, access_log, access_log_path, strport='tcp:22', |
768 | - factory_decorator=None, banner=None, moduli_path=None): |
769 | + def __init__( |
770 | + self, |
771 | + portal, |
772 | + private_key_path, |
773 | + public_key_path, |
774 | + main_log, |
775 | + access_log, |
776 | + access_log_path, |
777 | + strport="tcp:22", |
778 | + factory_decorator=None, |
779 | + banner=None, |
780 | + moduli_path=None, |
781 | + ): |
782 | """Construct an SSH service. |
783 | |
784 | :param portal: The `twisted.cred.portal.Portal` that turns |
785 | @@ -189,7 +195,8 @@ class SSHService(service.Service): |
786 | private_key=Key.fromFile(private_key_path), |
787 | public_key=Key.fromFile(public_key_path), |
788 | banner=banner, |
789 | - moduli_path=moduli_path) |
790 | + moduli_path=moduli_path, |
791 | + ) |
792 | if factory_decorator is not None: |
793 | ssh_factory = factory_decorator(ssh_factory) |
794 | self.service = strports.service(strport, ssh_factory) |
795 | @@ -202,7 +209,8 @@ class SSHService(service.Service): |
796 | self.manager = accesslog.LoggingManager( |
797 | logging.getLogger(self._main_log), |
798 | logging.getLogger(self._access_log_path), |
799 | - self._access_log_path) |
800 | + self._access_log_path, |
801 | + ) |
802 | self.manager.setUp() |
803 | notify(events.ServerStarting()) |
804 | # By default, only the owner of files should be able to write to them. |
805 | @@ -214,9 +222,12 @@ class SSHService(service.Service): |
806 | |
807 | def stopService(self): |
808 | """Stop the SSH service.""" |
809 | - deferred = gatherResults([ |
810 | - defer.maybeDeferred(service.Service.stopService, self), |
811 | - defer.maybeDeferred(self.service.stopService)]) |
812 | + deferred = gatherResults( |
813 | + [ |
814 | + defer.maybeDeferred(service.Service.stopService, self), |
815 | + defer.maybeDeferred(self.service.stopService), |
816 | + ] |
817 | + ) |
818 | |
819 | def log_stopped(ignored): |
820 | notify(events.ServerStopped()) |
821 | diff --git a/src/lazr/sshserver/session.py b/src/lazr/sshserver/session.py |
822 | index 44c20a2..29bde10 100644 |
823 | --- a/src/lazr/sshserver/session.py |
824 | +++ b/src/lazr/sshserver/session.py |
825 | @@ -7,16 +7,12 @@ from __future__ import absolute_import, print_function |
826 | |
827 | __metaclass__ = type |
828 | __all__ = [ |
829 | - 'DoNothingSession', |
830 | - 'PatchedSSHSession', |
831 | - ] |
832 | + "DoNothingSession", |
833 | + "PatchedSSHSession", |
834 | +] |
835 | |
836 | from twisted.conch.interfaces import ISession |
837 | -from twisted.conch.ssh import ( |
838 | - channel, |
839 | - connection, |
840 | - session, |
841 | - ) |
842 | +from twisted.conch.ssh import channel, connection, session |
843 | from zope.interface import implementer |
844 | |
845 | |
846 | @@ -42,7 +38,7 @@ class PatchedSSHSession(session.SSHSession, object): |
847 | # transport even if it's None. I don't know *why* it is None, so this |
848 | # doesn't necessarily address the root cause. |
849 | # See http://twistedmatrix.com/trac/ticket/2754. |
850 | - transport = getattr(self.client, 'transport', None) |
851 | + transport = getattr(self.client, "transport", None) |
852 | if transport is not None: |
853 | transport.loseConnection() |
854 | # This is called by session.SSHSession.loseConnection. SSHChannel is |
855 | @@ -59,12 +55,12 @@ class PatchedSSHSession(session.SSHSession, object): |
856 | # self.client.transport is entirely paranoia inspired by the comment |
857 | # in `loseConnection` above. It would be good to know if and why it is |
858 | # necessary. See http://twistedmatrix.com/trac/ticket/2754. |
859 | - transport = getattr(self.client, 'transport', None) |
860 | + transport = getattr(self.client, "transport", None) |
861 | if transport is not None: |
862 | # For SFTP connections, 'transport' is actually a _DummyTransport |
863 | # instance. Neither _DummyTransport nor the protocol it wraps |
864 | # (filetransfer.FileTransferServer) support pausing. |
865 | - pauseProducing = getattr(transport, 'pauseProducing', None) |
866 | + pauseProducing = getattr(transport, "pauseProducing", None) |
867 | if pauseProducing is not None: |
868 | pauseProducing() |
869 | |
870 | @@ -78,12 +74,12 @@ class PatchedSSHSession(session.SSHSession, object): |
871 | # self.client.transport is entirely paranoia inspired by the comment |
872 | # in `loseConnection` above. It would be good to know if and why it is |
873 | # necessary. See http://twistedmatrix.com/trac/ticket/2754. |
874 | - transport = getattr(self.client, 'transport', None) |
875 | + transport = getattr(self.client, "transport", None) |
876 | if transport is not None: |
877 | # For SFTP connections, 'transport' is actually a _DummyTransport |
878 | # instance. Neither _DummyTransport nor the protocol it wraps |
879 | # (filetransfer.FileTransferServer) support pausing. |
880 | - resumeProducing = getattr(transport, 'resumeProducing', None) |
881 | + resumeProducing = getattr(transport, "resumeProducing", None) |
882 | if resumeProducing is not None: |
883 | resumeProducing() |
884 | |
885 | @@ -102,14 +98,14 @@ class DoNothingSession: |
886 | """See ISession.""" |
887 | |
888 | def errorWithMessage(self, protocol, msg): |
889 | - protocol.session.writeExtended( |
890 | - connection.EXTENDED_DATA_STDERR, msg) |
891 | + protocol.session.writeExtended(connection.EXTENDED_DATA_STDERR, msg) |
892 | protocol.loseConnection() |
893 | |
894 | def execCommand(self, protocol, command): |
895 | """See ISession.""" |
896 | self.errorWithMessage( |
897 | - protocol, "Not allowed to execute commands on this server.\r\n") |
898 | + protocol, "Not allowed to execute commands on this server.\r\n" |
899 | + ) |
900 | |
901 | def getPty(self, term, windowSize, modes): |
902 | """See ISession.""" |
903 | diff --git a/src/lazr/sshserver/sftp.py b/src/lazr/sshserver/sftp.py |
904 | index 64c54c7..1335ce8 100644 |
905 | --- a/src/lazr/sshserver/sftp.py |
906 | +++ b/src/lazr/sshserver/sftp.py |
907 | @@ -7,9 +7,9 @@ from __future__ import absolute_import, print_function |
908 | |
909 | __metaclass__ = type |
910 | __all__ = [ |
911 | - 'FileIsADirectory', |
912 | - 'FileTransferServer', |
913 | - ] |
914 | + "FileIsADirectory", |
915 | + "FileTransferServer", |
916 | +] |
917 | |
918 | from twisted.conch.ssh import filetransfer |
919 | from zope.event import notify |
920 | @@ -26,7 +26,8 @@ class FileIsADirectory(Exception): |
921 | def __init__(self, path, extra=None): |
922 | self.path = path |
923 | super(FileIsADirectory, self).__init__( |
924 | - "File is a directory: %r" % path) |
925 | + "File is a directory: %r" % path |
926 | + ) |
927 | |
928 | |
929 | class FileTransferServer(filetransfer.FileTransferServer): |
930 | diff --git a/src/lazr/sshserver/tests/test_accesslog.py b/src/lazr/sshserver/tests/test_accesslog.py |
931 | index 5b8a11a..5dad2f6 100644 |
932 | --- a/src/lazr/sshserver/tests/test_accesslog.py |
933 | +++ b/src/lazr/sshserver/tests/test_accesslog.py |
934 | @@ -8,10 +8,10 @@ from __future__ import absolute_import, print_function |
935 | __metaclass__ = type |
936 | |
937 | import logging |
938 | -from logging.handlers import WatchedFileHandler |
939 | import os |
940 | import sys |
941 | import tempfile |
942 | +from logging.handlers import WatchedFileHandler |
943 | |
944 | try: |
945 | from bzrlib.tests import TestCase as BzrTestCase |
946 | @@ -31,11 +31,12 @@ class LoggingManagerMixin: |
947 | def makeLogger(self, name=None): |
948 | if name is None: |
949 | self._log_count += 1 |
950 | - name = '%s-%s' % (self.id().split('.')[-1], self._log_count) |
951 | + name = "%s-%s" % (self.id().split(".")[-1], self._log_count) |
952 | return logging.getLogger(name) |
953 | |
954 | - def installLoggingManager(self, main_log=None, access_log=None, |
955 | - access_log_path=None): |
956 | + def installLoggingManager( |
957 | + self, main_log=None, access_log=None, access_log_path=None |
958 | + ): |
959 | if main_log is None: |
960 | main_log = self.makeLogger() |
961 | if access_log is None: |
962 | @@ -51,17 +52,17 @@ class LoggingManagerMixin: |
963 | |
964 | |
965 | if BzrTestCase is not None: |
966 | - class TestLoggingBazaarInteraction(BzrTestCase, LoggingManagerMixin): |
967 | |
968 | + class TestLoggingBazaarInteraction(BzrTestCase, LoggingManagerMixin): |
969 | def test_leaves_bzr_handlers_unchanged(self): |
970 | # Bazaar's log handling is untouched by logging setup. |
971 | - root_handlers = logging.getLogger('').handlers |
972 | - bzr_handlers = logging.getLogger('bzr').handlers |
973 | + root_handlers = logging.getLogger("").handlers |
974 | + bzr_handlers = logging.getLogger("bzr").handlers |
975 | |
976 | self.apply_redirected(a_callable=self.installLoggingManager) |
977 | |
978 | - self.assertEqual(root_handlers, logging.getLogger('').handlers) |
979 | - self.assertEqual(bzr_handlers, logging.getLogger('bzr').handlers) |
980 | + self.assertEqual(root_handlers, logging.getLogger("").handlers) |
981 | + self.assertEqual(bzr_handlers, logging.getLogger("bzr").handlers) |
982 | |
983 | def test_log_doesnt_go_to_stderr(self): |
984 | # Once logging setup is called, any messages logged to the |
985 | @@ -73,15 +74,15 @@ if BzrTestCase is not None: |
986 | self.installLoggingManager(log) |
987 | |
988 | # Make sure that a logged message does not go to stderr. |
989 | - log.info('Hello hello') |
990 | - self.assertEqual(sys.stderr.getvalue(), '') |
991 | + log.info("Hello hello") |
992 | + self.assertEqual(sys.stderr.getvalue(), "") |
993 | |
994 | self.apply_redirected(a_callable=inner) |
995 | |
996 | |
997 | class TestLoggingManager( |
998 | - testtools.TestCase, fixtures.TestWithFixtures, LoggingManagerMixin): |
999 | - |
1000 | + testtools.TestCase, fixtures.TestWithFixtures, LoggingManagerMixin |
1001 | +): |
1002 | def test_main_log_handlers(self): |
1003 | # There needs to be at least one handler for the root logger. If there |
1004 | # isn't, we'll get constant errors complaining about the lack of |
1005 | @@ -93,10 +94,9 @@ class TestLoggingManager( |
1006 | |
1007 | def _get_handlers(self): |
1008 | registrations = ( |
1009 | - zope.component.getGlobalSiteManager().registeredHandlers()) |
1010 | - return [ |
1011 | - registration.factory |
1012 | - for registration in registrations] |
1013 | + zope.component.getGlobalSiteManager().registeredHandlers() |
1014 | + ) |
1015 | + return [registration.factory for registration in registrations] |
1016 | |
1017 | def test_set_up_registers_event_handler(self): |
1018 | manager = self.installLoggingManager() |
1019 | @@ -136,10 +136,10 @@ class TestLoggingManager( |
1020 | # to the SSH server access log. |
1021 | directory = self.useFixture(fixtures.TempDir()).path |
1022 | access_log = self.makeLogger() |
1023 | - access_log_path = os.path.join(directory, 'access.log') |
1024 | + access_log_path = os.path.join(directory, "access.log") |
1025 | self.installLoggingManager( |
1026 | - access_log=access_log, |
1027 | - access_log_path=access_log_path) |
1028 | + access_log=access_log, access_log_path=access_log_path |
1029 | + ) |
1030 | [handler] = access_log.handlers |
1031 | self.assertIsInstance(handler, WatchedFileHandler) |
1032 | self.assertEqual(access_log_path, handler.baseFilename) |
1033 | diff --git a/src/lazr/sshserver/tests/test_auth.py b/src/lazr/sshserver/tests/test_auth.py |
1034 | index d146a19..7fb7f21 100644 |
1035 | --- a/src/lazr/sshserver/tests/test_auth.py |
1036 | +++ b/src/lazr/sshserver/tests/test_auth.py |
1037 | @@ -13,31 +13,19 @@ import sys |
1038 | |
1039 | import testtools |
1040 | from testtools.deferredruntest import ( |
1041 | - assert_fails_with, |
1042 | AsynchronousDeferredRunTest, |
1043 | + assert_fails_with, |
1044 | flush_logged_errors, |
1045 | - ) |
1046 | +) |
1047 | from twisted.conch.error import ConchError |
1048 | from twisted.conch.ssh import userauth |
1049 | -from twisted.conch.ssh.common import ( |
1050 | - getNS, |
1051 | - NS, |
1052 | - ) |
1053 | -from twisted.conch.ssh.keys import ( |
1054 | - BadKeyError, |
1055 | - Key, |
1056 | - ) |
1057 | -from twisted.conch.ssh.transport import ( |
1058 | - SSHCiphers, |
1059 | - SSHServerTransport, |
1060 | - ) |
1061 | +from twisted.conch.ssh.common import NS, getNS |
1062 | +from twisted.conch.ssh.keys import BadKeyError, Key |
1063 | +from twisted.conch.ssh.transport import SSHCiphers, SSHServerTransport |
1064 | from twisted.cred.checkers import ICredentialsChecker |
1065 | from twisted.cred.credentials import ISSHPrivateKey |
1066 | from twisted.cred.error import UnauthorizedLogin |
1067 | -from twisted.cred.portal import ( |
1068 | - IRealm, |
1069 | - Portal, |
1070 | - ) |
1071 | +from twisted.cred.portal import IRealm, Portal |
1072 | from twisted.internet import defer |
1073 | from twisted.python import failure |
1074 | from twisted.python.util import sibpath |
1075 | @@ -47,8 +35,8 @@ from lazr.sshserver import auth |
1076 | |
1077 | |
1078 | def suppress_stderr(function): |
1079 | - """Deferred friendly decorator that suppresses output from a function. |
1080 | - """ |
1081 | + """Deferred friendly decorator that suppresses output from a function.""" |
1082 | + |
1083 | def set_stderr(result, stream): |
1084 | sys.stderr = stream |
1085 | return result |
1086 | @@ -77,10 +65,12 @@ class MockRealm: |
1087 | |
1088 | def requestAvatar(self, avatar_id, mind, *interfaces): |
1089 | user_dict = { |
1090 | - 'id': avatar_id, 'name': avatar_id, 'teams': [], |
1091 | - 'initialBranches': []} |
1092 | - return ( |
1093 | - interfaces[0], auth.LaunchpadAvatar(user_dict), lambda: None) |
1094 | + "id": avatar_id, |
1095 | + "name": avatar_id, |
1096 | + "teams": [], |
1097 | + "initialBranches": [], |
1098 | + } |
1099 | + return (interfaces[0], auth.LaunchpadAvatar(user_dict), lambda: None) |
1100 | |
1101 | |
1102 | class MockSSHTransport(SSHServerTransport): |
1103 | @@ -103,7 +93,7 @@ class MockSSHTransport(SSHServerTransport): |
1104 | # In Twisted 8.0.1, Conch's transport starts referring to |
1105 | # currentEncryptions where it didn't before. Provide a dummy value for |
1106 | # it. |
1107 | - self.currentEncryptions = SSHCiphers('none', 'none', 'none', 'none') |
1108 | + self.currentEncryptions = SSHCiphers("none", "none", "none", "none") |
1109 | self.packets = [] |
1110 | self.factory = self.Factory() |
1111 | self.factory.portal = portal |
1112 | @@ -131,25 +121,27 @@ class UserAuthServerMixin(object): |
1113 | messages = userauth.SSHUserAuthServer.protocolMessages |
1114 | self.assertEqual( |
1115 | [messages[msg_type] for msg_type in message_types], |
1116 | - [messages[packet_type] |
1117 | - for packet_type, contents in self.transport.packets]) |
1118 | + [ |
1119 | + messages[packet_type] |
1120 | + for packet_type, contents in self.transport.packets |
1121 | + ], |
1122 | + ) |
1123 | |
1124 | - def assertBannerSent(self, banner_message, expected_language='en'): |
1125 | + def assertBannerSent(self, banner_message, expected_language="en"): |
1126 | """Assert that 'banner_message' was sent as an SSH banner.""" |
1127 | # Check that we received a BANNER, then a FAILURE. |
1128 | for packet_type, packet_content in self.transport.packets: |
1129 | if packet_type == userauth.MSG_USERAUTH_BANNER: |
1130 | bytes, language, empty = getNS(packet_content, 2) |
1131 | - self.assertEqual(banner_message, bytes.decode('UTF8')) |
1132 | - self.assertEqual(expected_language, language.decode('UTF8')) |
1133 | - self.assertEqual(b'', empty) |
1134 | + self.assertEqual(banner_message, bytes.decode("UTF8")) |
1135 | + self.assertEqual(expected_language, language.decode("UTF8")) |
1136 | + self.assertEqual(b"", empty) |
1137 | break |
1138 | else: |
1139 | self.fail("No banner logged.") |
1140 | |
1141 | |
1142 | class TestUserAuthServer(UserAuthServerMixin, testtools.TestCase): |
1143 | - |
1144 | def test_sendBanner(self): |
1145 | # sendBanner should send an SSH 'packet' with type MSG_USERAUTH_BANNER |
1146 | # and two fields. The first field is the message itself, and the |
1147 | @@ -160,11 +152,13 @@ class TestUserAuthServer(UserAuthServerMixin, testtools.TestCase): |
1148 | # |
1149 | # See RFC 4252, Section 5.4. |
1150 | message = u"test message" |
1151 | - self.user_auth.sendBanner(message, language='en-US') |
1152 | - self.assertBannerSent(message + '\r\n', 'en-US') |
1153 | + self.user_auth.sendBanner(message, language="en-US") |
1154 | + self.assertBannerSent(message + "\r\n", "en-US") |
1155 | self.assertEqual( |
1156 | - 1, len(self.transport.packets), |
1157 | - "More than just banner was sent: %r" % self.transport.packets) |
1158 | + 1, |
1159 | + len(self.transport.packets), |
1160 | + "More than just banner was sent: %r" % self.transport.packets, |
1161 | + ) |
1162 | |
1163 | def test_sendBannerUsesCRLF(self): |
1164 | # sendBanner should make sure that any line breaks in the message are |
1165 | @@ -174,7 +168,7 @@ class TestUserAuthServer(UserAuthServerMixin, testtools.TestCase): |
1166 | self.user_auth.sendBanner(u"test\nmessage") |
1167 | [(messageType, payload)] = self.transport.packets |
1168 | bytes, language, empty = getNS(payload, 2) |
1169 | - self.assertEqual(bytes.decode('UTF8'), u"test\r\nmessage\r\n") |
1170 | + self.assertEqual(bytes.decode("UTF8"), u"test\r\nmessage\r\n") |
1171 | |
1172 | def test_requestRaisesConchError(self): |
1173 | # ssh_USERAUTH_REQUEST should raise a ConchError if tryAuth returns |
1174 | @@ -188,11 +182,13 @@ class TestUserAuthServer(UserAuthServerMixin, testtools.TestCase): |
1175 | |
1176 | tryAuth = self.user_auth.tryAuth |
1177 | self.user_auth.tryAuth = mock_try_auth |
1178 | - _ebBadAuth, self.user_auth._ebBadAuth = (self.user_auth._ebBadAuth, |
1179 | - mock_eb_bad_auth) |
1180 | + _ebBadAuth, self.user_auth._ebBadAuth = ( |
1181 | + self.user_auth._ebBadAuth, |
1182 | + mock_eb_bad_auth, |
1183 | + ) |
1184 | self.user_auth.serviceStarted() |
1185 | try: |
1186 | - packet = NS(b'jml') + NS(b'foo') + NS(b'public_key') + NS(b'data') |
1187 | + packet = NS(b"jml") + NS(b"foo") + NS(b"public_key") + NS(b"data") |
1188 | self.user_auth.ssh_USERAUTH_REQUEST(packet) |
1189 | finally: |
1190 | self.user_auth.serviceStopped() |
1191 | @@ -202,20 +198,25 @@ class TestUserAuthServer(UserAuthServerMixin, testtools.TestCase): |
1192 | def test_handlesBadKey(self): |
1193 | # auth_publickey handles the case where Twisted fails to load the |
1194 | # user-supplied public key. |
1195 | - self.transport.sessionID = '' |
1196 | + self.transport.sessionID = "" |
1197 | packet = ( |
1198 | - NS(b'user') + NS(b'') + NS(b'publickey') + |
1199 | - b'\x01' + NS(b'ssh-rsa') + NS(b'badkey') + |
1200 | - NS(NS(b'ssh-rsa') + NS(b'\x01' * 128))) |
1201 | + NS(b"user") |
1202 | + + NS(b"") |
1203 | + + NS(b"publickey") |
1204 | + + b"\x01" |
1205 | + + NS(b"ssh-rsa") |
1206 | + + NS(b"badkey") |
1207 | + + NS(NS(b"ssh-rsa") + NS(b"\x01" * 128)) |
1208 | + ) |
1209 | self.user_auth.serviceStarted() |
1210 | - self.user_auth.supportedAuthentications.append(b'publickey') |
1211 | + self.user_auth.supportedAuthentications.append(b"publickey") |
1212 | try: |
1213 | self.user_auth.ssh_USERAUTH_REQUEST(packet) |
1214 | [(packet_type, packet_content)] = self.transport.packets |
1215 | self.assertEqual(userauth.MSG_USERAUTH_FAILURE, packet_type) |
1216 | name_list, partial_success = getNS(packet_content) |
1217 | - self.assertEqual(b'publickey', name_list) |
1218 | - self.assertEqual(b'\x00', partial_success) |
1219 | + self.assertEqual(b"publickey", name_list) |
1220 | + self.assertEqual(b"\x00", partial_success) |
1221 | finally: |
1222 | self.user_auth.serviceStopped() |
1223 | |
1224 | @@ -229,19 +230,27 @@ class TestUserAuthServer(UserAuthServerMixin, testtools.TestCase): |
1225 | |
1226 | def __call__(self, credentials, mind, *interfaces): |
1227 | self.credentials = credentials |
1228 | - return defer.succeed(self.portal.realm.requestAvatar( |
1229 | - credentials.username, mind, *interfaces)) |
1230 | + return defer.succeed( |
1231 | + self.portal.realm.requestAvatar( |
1232 | + credentials.username, mind, *interfaces |
1233 | + ) |
1234 | + ) |
1235 | |
1236 | - self.transport.sessionID = '' |
1237 | + self.transport.sessionID = "" |
1238 | self.portal.login = MockLogin(self.portal) |
1239 | - keydir = sibpath(__file__, 'keys') |
1240 | - with open(os.path.join(keydir, 'ssh_host_key_rsa.pub'), 'rb') as f: |
1241 | + keydir = sibpath(__file__, "keys") |
1242 | + with open(os.path.join(keydir, "ssh_host_key_rsa.pub"), "rb") as f: |
1243 | public_key = Key.fromString(f.read()) |
1244 | packet = ( |
1245 | - NS(b'user') + NS(b'') + NS(b'publickey') + |
1246 | - b'\x00' + NS(b'ssh-rsa') + NS(public_key.blob())) |
1247 | + NS(b"user") |
1248 | + + NS(b"") |
1249 | + + NS(b"publickey") |
1250 | + + b"\x00" |
1251 | + + NS(b"ssh-rsa") |
1252 | + + NS(public_key.blob()) |
1253 | + ) |
1254 | self.user_auth.serviceStarted() |
1255 | - self.user_auth.supportedAuthentications.append(b'publickey') |
1256 | + self.user_auth.supportedAuthentications.append(b"publickey") |
1257 | try: |
1258 | self.user_auth.ssh_USERAUTH_REQUEST(packet) |
1259 | self.assertIsNone(self.portal.login.credentials.signature) |
1260 | @@ -257,27 +266,36 @@ class TestUserAuthServer(UserAuthServerMixin, testtools.TestCase): |
1261 | |
1262 | def __call__(self, credentials, mind, *interfaces): |
1263 | self.credentials = credentials |
1264 | - return defer.succeed(self.portal.realm.requestAvatar( |
1265 | - credentials.username, mind, *interfaces)) |
1266 | + return defer.succeed( |
1267 | + self.portal.realm.requestAvatar( |
1268 | + credentials.username, mind, *interfaces |
1269 | + ) |
1270 | + ) |
1271 | |
1272 | - self.transport.sessionID = '' |
1273 | + self.transport.sessionID = "" |
1274 | self.portal.login = MockLogin(self.portal) |
1275 | - keydir = sibpath(__file__, 'keys') |
1276 | - with open(os.path.join(keydir, 'ssh_host_key_rsa.pub'), 'rb') as f: |
1277 | + keydir = sibpath(__file__, "keys") |
1278 | + with open(os.path.join(keydir, "ssh_host_key_rsa.pub"), "rb") as f: |
1279 | public_key = Key.fromString(f.read()) |
1280 | public_key_len = (public_key.size() + 7) // 8 |
1281 | - signature = b'\x01' * (public_key_len - 1) |
1282 | + signature = b"\x01" * (public_key_len - 1) |
1283 | packet = ( |
1284 | - NS(b'user') + NS(b'') + NS(b'publickey') + |
1285 | - b'\x01' + NS(b'ssh-rsa') + NS(public_key.blob()) + |
1286 | - NS(NS(b'ssh-rsa') + NS(signature))) |
1287 | + NS(b"user") |
1288 | + + NS(b"") |
1289 | + + NS(b"publickey") |
1290 | + + b"\x01" |
1291 | + + NS(b"ssh-rsa") |
1292 | + + NS(public_key.blob()) |
1293 | + + NS(NS(b"ssh-rsa") + NS(signature)) |
1294 | + ) |
1295 | self.user_auth.serviceStarted() |
1296 | - self.user_auth.supportedAuthentications.append(b'publickey') |
1297 | + self.user_auth.supportedAuthentications.append(b"publickey") |
1298 | try: |
1299 | self.user_auth.ssh_USERAUTH_REQUEST(packet) |
1300 | self.assertEqual( |
1301 | - NS(b'ssh-rsa') + NS(b'\x00' + signature), |
1302 | - self.portal.login.credentials.signature) |
1303 | + NS(b"ssh-rsa") + NS(b"\x00" + signature), |
1304 | + self.portal.login.credentials.signature, |
1305 | + ) |
1306 | finally: |
1307 | self.user_auth.serviceStopped() |
1308 | |
1309 | @@ -292,14 +310,15 @@ class MockChecker: |
1310 | |
1311 | credentialInterfaces = (ISSHPrivateKey,) |
1312 | |
1313 | - error_message = u'error message' |
1314 | + error_message = u"error message" |
1315 | |
1316 | def requestAvatarId(self, credentials): |
1317 | - if credentials.username == b'success': |
1318 | + if credentials.username == b"success": |
1319 | return credentials.username |
1320 | else: |
1321 | return failure.Failure( |
1322 | - auth.UserDisplayedUnauthorizedLogin(self.error_message)) |
1323 | + auth.UserDisplayedUnauthorizedLogin(self.error_message) |
1324 | + ) |
1325 | |
1326 | |
1327 | class TestAuthenticationBannerDisplay(UserAuthServerMixin, testtools.TestCase): |
1328 | @@ -327,27 +346,30 @@ class TestAuthenticationBannerDisplay(UserAuthServerMixin, testtools.TestCase): |
1329 | super(TestAuthenticationBannerDisplay, self).tearDown() |
1330 | |
1331 | def _makeKey(self): |
1332 | - keydir = sibpath(__file__, 'keys') |
1333 | - with open(os.path.join(keydir, 'ssh_host_key_rsa.pub'), 'rb') as f: |
1334 | + keydir = sibpath(__file__, "keys") |
1335 | + with open(os.path.join(keydir, "ssh_host_key_rsa.pub"), "rb") as f: |
1336 | public_key = Key.fromString(f.read()) |
1337 | if isinstance(public_key, str): |
1338 | - return b'\x00' + NS(b'rsa') + NS(public_key) |
1339 | + return b"\x00" + NS(b"rsa") + NS(public_key) |
1340 | else: |
1341 | - return b'\x00' + NS(b'rsa') + NS(public_key.blob()) |
1342 | + return b"\x00" + NS(b"rsa") + NS(public_key.blob()) |
1343 | |
1344 | def requestFailedAuthentication(self): |
1345 | return self.user_auth.ssh_USERAUTH_REQUEST( |
1346 | - NS(b'failure') + NS(b'') + NS(b'publickey') + self.key_data) |
1347 | + NS(b"failure") + NS(b"") + NS(b"publickey") + self.key_data |
1348 | + ) |
1349 | |
1350 | def requestSuccessfulAuthentication(self): |
1351 | return self.user_auth.ssh_USERAUTH_REQUEST( |
1352 | - NS(b'success') + NS(b'') + NS(b'publickey') + self.key_data) |
1353 | + NS(b"success") + NS(b"") + NS(b"publickey") + self.key_data |
1354 | + ) |
1355 | |
1356 | def requestUnsupportedAuthentication(self): |
1357 | # Note that it doesn't matter how the checker responds -- the server |
1358 | # doesn't get that far. |
1359 | return self.user_auth.ssh_USERAUTH_REQUEST( |
1360 | - NS(b'success') + NS(b'') + NS(b'none') + NS(b'')) |
1361 | + NS(b"success") + NS(b"") + NS(b"none") + NS(b"") |
1362 | + ) |
1363 | |
1364 | @defer.inlineCallbacks |
1365 | def test_bannerNotSentOnSuccess(self): |
1366 | @@ -366,8 +388,9 @@ class TestAuthenticationBannerDisplay(UserAuthServerMixin, testtools.TestCase): |
1367 | self.user_auth._banner = "Boogedy boo" |
1368 | yield self.requestSuccessfulAuthentication() |
1369 | self.assertMessageOrder( |
1370 | - [userauth.MSG_USERAUTH_BANNER, userauth.MSG_USERAUTH_SUCCESS]) |
1371 | - self.assertBannerSent(self.user_auth._banner + '\r\n') |
1372 | + [userauth.MSG_USERAUTH_BANNER, userauth.MSG_USERAUTH_SUCCESS] |
1373 | + ) |
1374 | + self.assertBannerSent(self.user_auth._banner + "\r\n") |
1375 | |
1376 | @defer.inlineCallbacks |
1377 | def test_defaultBannerSentOnlyOnce(self): |
1378 | @@ -381,9 +404,13 @@ class TestAuthenticationBannerDisplay(UserAuthServerMixin, testtools.TestCase): |
1379 | |
1380 | # Check that no banner was sent to the user. |
1381 | self.assertMessageOrder( |
1382 | - [userauth.MSG_USERAUTH_FAILURE, userauth.MSG_USERAUTH_BANNER, |
1383 | - userauth.MSG_USERAUTH_SUCCESS]) |
1384 | - self.assertBannerSent(self.user_auth._banner + '\r\n') |
1385 | + [ |
1386 | + userauth.MSG_USERAUTH_FAILURE, |
1387 | + userauth.MSG_USERAUTH_BANNER, |
1388 | + userauth.MSG_USERAUTH_SUCCESS, |
1389 | + ] |
1390 | + ) |
1391 | + self.assertBannerSent(self.user_auth._banner + "\r\n") |
1392 | |
1393 | @defer.inlineCallbacks |
1394 | def test_defaultBannerNotSentOnFailure(self): |
1395 | @@ -394,8 +421,9 @@ class TestAuthenticationBannerDisplay(UserAuthServerMixin, testtools.TestCase): |
1396 | yield self.requestFailedAuthentication() |
1397 | |
1398 | self.assertMessageOrder( |
1399 | - [userauth.MSG_USERAUTH_BANNER, userauth.MSG_USERAUTH_FAILURE]) |
1400 | - self.assertBannerSent(MockChecker.error_message + '\r\n') |
1401 | + [userauth.MSG_USERAUTH_BANNER, userauth.MSG_USERAUTH_FAILURE] |
1402 | + ) |
1403 | + self.assertBannerSent(MockChecker.error_message + "\r\n") |
1404 | |
1405 | @defer.inlineCallbacks |
1406 | def test_loggedToBanner(self): |
1407 | @@ -404,8 +432,9 @@ class TestAuthenticationBannerDisplay(UserAuthServerMixin, testtools.TestCase): |
1408 | yield self.requestFailedAuthentication() |
1409 | # Check that we received a BANNER, then a FAILURE. |
1410 | self.assertMessageOrder( |
1411 | - [userauth.MSG_USERAUTH_BANNER, userauth.MSG_USERAUTH_FAILURE]) |
1412 | - self.assertBannerSent(MockChecker.error_message + '\r\n') |
1413 | + [userauth.MSG_USERAUTH_BANNER, userauth.MSG_USERAUTH_FAILURE] |
1414 | + ) |
1415 | + self.assertBannerSent(MockChecker.error_message + "\r\n") |
1416 | |
1417 | @defer.inlineCallbacks |
1418 | def test_unsupportedAuthMethodNotLogged(self): |
1419 | @@ -431,40 +460,45 @@ class TestPublicKeyFromLaunchpadChecker(testtools.TestCase): |
1420 | run_tests_with = AsynchronousDeferredRunTest |
1421 | |
1422 | class FakeAuthenticationEndpoint: |
1423 | - """A fake client for enough of `IAuthServer` for this test. |
1424 | - """ |
1425 | + """A fake client for enough of `IAuthServer` for this test.""" |
1426 | |
1427 | - valid_user = 'valid_user' |
1428 | - no_key_user = 'no_key_user' |
1429 | - valid_key_rsa = b'valid_key_rsa' |
1430 | - valid_key_dsa = b'valid_key_dsa' |
1431 | - valid_key_ecdsa = b'valid_key_ecdsa' |
1432 | - valid_key_ed25519 = b'valid_key_ed25519' |
1433 | + valid_user = "valid_user" |
1434 | + no_key_user = "no_key_user" |
1435 | + valid_key_rsa = b"valid_key_rsa" |
1436 | + valid_key_dsa = b"valid_key_dsa" |
1437 | + valid_key_ecdsa = b"valid_key_ecdsa" |
1438 | + valid_key_ed25519 = b"valid_key_ed25519" |
1439 | |
1440 | def __init__(self): |
1441 | self.calls = [] |
1442 | |
1443 | def callRemote(self, function_name, *args, **kwargs): |
1444 | - return getattr( |
1445 | - self, 'xmlrpc_%s' % function_name)(*args, **kwargs) |
1446 | + return getattr(self, "xmlrpc_%s" % function_name)(*args, **kwargs) |
1447 | |
1448 | def xmlrpc_getUserAndSSHKeys(self, username): |
1449 | self.calls.append(username) |
1450 | if username == self.valid_user: |
1451 | - return defer.succeed({ |
1452 | - 'name': username, |
1453 | - 'keys': [ |
1454 | - ('RSA', base64.b64encode(self.valid_key_rsa)), |
1455 | - ('DSA', base64.b64encode(self.valid_key_dsa)), |
1456 | - ('ECDSA', base64.b64encode(self.valid_key_ecdsa)), |
1457 | - ('ED25519', base64.b64encode(self.valid_key_ed25519)), |
1458 | + return defer.succeed( |
1459 | + { |
1460 | + "name": username, |
1461 | + "keys": [ |
1462 | + ("RSA", base64.b64encode(self.valid_key_rsa)), |
1463 | + ("DSA", base64.b64encode(self.valid_key_dsa)), |
1464 | + ("ECDSA", base64.b64encode(self.valid_key_ecdsa)), |
1465 | + ( |
1466 | + "ED25519", |
1467 | + base64.b64encode(self.valid_key_ed25519), |
1468 | + ), |
1469 | ], |
1470 | - }) |
1471 | + } |
1472 | + ) |
1473 | elif username == self.no_key_user: |
1474 | - return defer.succeed({ |
1475 | - 'name': username, |
1476 | - 'keys': [], |
1477 | - }) |
1478 | + return defer.succeed( |
1479 | + { |
1480 | + "name": username, |
1481 | + "keys": [], |
1482 | + } |
1483 | + ) |
1484 | else: |
1485 | try: |
1486 | raise auth.NoSuchPersonWithName(username) |
1487 | @@ -475,7 +509,8 @@ class TestPublicKeyFromLaunchpadChecker(testtools.TestCase): |
1488 | if mind is None: |
1489 | mind = auth.UserDetailsMind() |
1490 | return auth.SSHPrivateKeyWithMind( |
1491 | - username, key_type, public_key, '', None, mind) |
1492 | + username, key_type, public_key, "", None, mind |
1493 | + ) |
1494 | |
1495 | def makeChecker(self, do_signature_checking=False): |
1496 | """Construct a PublicKeyFromLaunchpadChecker. |
1497 | @@ -502,27 +537,32 @@ class TestPublicKeyFromLaunchpadChecker(testtools.TestCase): |
1498 | # Attempting to log in with a username and key known to the |
1499 | # authentication end-point succeeds. |
1500 | for key_type, public_key in ( |
1501 | - (b'ssh-rsa', self.authserver.valid_key_rsa), |
1502 | - (b'ssh-dss', self.authserver.valid_key_dsa), |
1503 | - (b'ecdsa-sha2-nistp256', self.authserver.valid_key_ecdsa), |
1504 | - (b'ssh-ed25519', self.authserver.valid_key_ed25519), |
1505 | - ): |
1506 | + (b"ssh-rsa", self.authserver.valid_key_rsa), |
1507 | + (b"ssh-dss", self.authserver.valid_key_dsa), |
1508 | + (b"ecdsa-sha2-nistp256", self.authserver.valid_key_ecdsa), |
1509 | + (b"ssh-ed25519", self.authserver.valid_key_ed25519), |
1510 | + ): |
1511 | creds = self.makeCredentials( |
1512 | - self.authserver.valid_user.encode('UTF-8'), key_type, |
1513 | - public_key) |
1514 | + self.authserver.valid_user.encode("UTF-8"), |
1515 | + key_type, |
1516 | + public_key, |
1517 | + ) |
1518 | checker = self.makeChecker() |
1519 | username = yield checker.requestAvatarId(creds) |
1520 | self.assertEqual( |
1521 | - self.authserver.valid_user.encode('UTF-8'), username) |
1522 | + self.authserver.valid_user.encode("UTF-8"), username |
1523 | + ) |
1524 | |
1525 | @suppress_stderr |
1526 | def test_invalid_signature(self): |
1527 | # The checker requests attempts to authenticate if the requests have |
1528 | # an invalid signature. |
1529 | creds = self.makeCredentials( |
1530 | - self.authserver.valid_user.encode('UTF-8'), b'ssh-dss', |
1531 | - self.authserver.valid_key_dsa) |
1532 | - creds.signature = 'a' |
1533 | + self.authserver.valid_user.encode("UTF-8"), |
1534 | + b"ssh-dss", |
1535 | + self.authserver.valid_key_dsa, |
1536 | + ) |
1537 | + creds.signature = "a" |
1538 | checker = self.makeChecker(True) |
1539 | d = checker.requestAvatarId(creds) |
1540 | |
1541 | @@ -545,19 +585,22 @@ class TestPublicKeyFromLaunchpadChecker(testtools.TestCase): |
1542 | :return: Deferred. You must return this from your test. |
1543 | """ |
1544 | d = assert_fails_with( |
1545 | - checker.requestAvatarId(creds), |
1546 | - auth.UserDisplayedUnauthorizedLogin) |
1547 | + checker.requestAvatarId(creds), auth.UserDisplayedUnauthorizedLogin |
1548 | + ) |
1549 | d.addCallback( |
1550 | - lambda exception: self.assertEqual(str(exception), error_message)) |
1551 | + lambda exception: self.assertEqual(str(exception), error_message) |
1552 | + ) |
1553 | return d |
1554 | |
1555 | def test_badUserEncoding(self): |
1556 | # Attempting to sign in using a non-UTF-8 user name fails. |
1557 | checker = self.makeChecker() |
1558 | creds = self.makeCredentials( |
1559 | - b'\x80', b'ssh-dss', self.authserver.valid_key_dsa) |
1560 | + b"\x80", b"ssh-dss", self.authserver.valid_key_dsa |
1561 | + ) |
1562 | return self.assertLoginError( |
1563 | - checker, creds, 'No such Launchpad account: %r' % b'\x80') |
1564 | + checker, creds, "No such Launchpad account: %r" % b"\x80" |
1565 | + ) |
1566 | |
1567 | def test_noSuchUser(self): |
1568 | # When someone signs in with a non-existent user, they should be told |
1569 | @@ -565,21 +608,27 @@ class TestPublicKeyFromLaunchpadChecker(testtools.TestCase): |
1570 | # Launchpad user names is public. |
1571 | checker = self.makeChecker() |
1572 | creds = self.makeCredentials( |
1573 | - b'no-such-user', b'ssh-dss', self.authserver.valid_key_dsa) |
1574 | + b"no-such-user", b"ssh-dss", self.authserver.valid_key_dsa |
1575 | + ) |
1576 | return self.assertLoginError( |
1577 | - checker, creds, 'No such Launchpad account: no-such-user') |
1578 | + checker, creds, "No such Launchpad account: no-such-user" |
1579 | + ) |
1580 | |
1581 | def test_noKeys(self): |
1582 | # When you sign into an existing account with no SSH keys, the SSH |
1583 | # server informs you that the account has no keys. |
1584 | checker = self.makeChecker() |
1585 | creds = self.makeCredentials( |
1586 | - self.authserver.no_key_user.encode('UTF-8'), b'ssh-dss', |
1587 | - self.authserver.valid_key_dsa) |
1588 | + self.authserver.no_key_user.encode("UTF-8"), |
1589 | + b"ssh-dss", |
1590 | + self.authserver.valid_key_dsa, |
1591 | + ) |
1592 | return self.assertLoginError( |
1593 | - checker, creds, |
1594 | + checker, |
1595 | + creds, |
1596 | "Launchpad user '%s' doesn't have a registered SSH key" |
1597 | - % self.authserver.no_key_user) |
1598 | + % self.authserver.no_key_user, |
1599 | + ) |
1600 | |
1601 | def test_wrongKey(self): |
1602 | # When you sign into an existing account using the wrong key, you |
1603 | @@ -587,34 +636,40 @@ class TestPublicKeyFromLaunchpadChecker(testtools.TestCase): |
1604 | # tries several keys as part of normal operation. |
1605 | checker = self.makeChecker() |
1606 | creds = self.makeCredentials( |
1607 | - self.authserver.valid_user.encode('UTF-8'), b'ssh-dss', |
1608 | - b'invalid key') |
1609 | + self.authserver.valid_user.encode("UTF-8"), |
1610 | + b"ssh-dss", |
1611 | + b"invalid key", |
1612 | + ) |
1613 | # We cannot use assertLoginError because we are checking that we fail |
1614 | # with UnauthorizedLogin and not its subclass |
1615 | # UserDisplayedUnauthorizedLogin. |
1616 | d = assert_fails_with( |
1617 | - checker.requestAvatarId(creds), |
1618 | - UnauthorizedLogin) |
1619 | + checker.requestAvatarId(creds), UnauthorizedLogin |
1620 | + ) |
1621 | d.addCallback( |
1622 | - lambda exception: |
1623 | - self.assertFalse( |
1624 | + lambda exception: self.assertFalse( |
1625 | isinstance(exception, auth.UserDisplayedUnauthorizedLogin), |
1626 | - "Should not be a UserDisplayedUnauthorizedLogin")) |
1627 | + "Should not be a UserDisplayedUnauthorizedLogin", |
1628 | + ) |
1629 | + ) |
1630 | return d |
1631 | |
1632 | @defer.inlineCallbacks |
1633 | def test_unknownKeyType(self): |
1634 | # Authenticating using a key with an unknown type fails. |
1635 | creds = self.makeCredentials( |
1636 | - self.authserver.valid_user.encode('UTF-8'), b'nonsense', |
1637 | - self.authserver.valid_key_rsa) |
1638 | + self.authserver.valid_user.encode("UTF-8"), |
1639 | + b"nonsense", |
1640 | + self.authserver.valid_key_rsa, |
1641 | + ) |
1642 | checker = self.makeChecker() |
1643 | exception = yield assert_fails_with( |
1644 | - checker.requestAvatarId(creds), |
1645 | - UnauthorizedLogin) |
1646 | + checker.requestAvatarId(creds), UnauthorizedLogin |
1647 | + ) |
1648 | self.assertFalse( |
1649 | isinstance(exception, auth.UserDisplayedUnauthorizedLogin), |
1650 | - "Should not be a UserDisplayedUnauthorizedLogin") |
1651 | + "Should not be a UserDisplayedUnauthorizedLogin", |
1652 | + ) |
1653 | |
1654 | @defer.inlineCallbacks |
1655 | def test_successful_with_second_key_calls_authserver_once(self): |
1656 | @@ -625,16 +680,22 @@ class TestPublicKeyFromLaunchpadChecker(testtools.TestCase): |
1657 | checker = self.makeChecker() |
1658 | mind = auth.UserDetailsMind() |
1659 | wrong_key_creds = self.makeCredentials( |
1660 | - self.authserver.valid_user.encode('UTF-8'), b'ssh-dss', |
1661 | - b'invalid key', mind) |
1662 | + self.authserver.valid_user.encode("UTF-8"), |
1663 | + b"ssh-dss", |
1664 | + b"invalid key", |
1665 | + mind, |
1666 | + ) |
1667 | right_key_creds = self.makeCredentials( |
1668 | - self.authserver.valid_user.encode('UTF-8'), b'ssh-dss', |
1669 | - self.authserver.valid_key_dsa, mind) |
1670 | + self.authserver.valid_user.encode("UTF-8"), |
1671 | + b"ssh-dss", |
1672 | + self.authserver.valid_key_dsa, |
1673 | + mind, |
1674 | + ) |
1675 | try: |
1676 | username = yield checker.requestAvatarId(wrong_key_creds) |
1677 | except UnauthorizedLogin: |
1678 | username = yield checker.requestAvatarId(right_key_creds) |
1679 | - self.assertEqual(self.authserver.valid_user.encode('UTF-8'), username) |
1680 | + self.assertEqual(self.authserver.valid_user.encode("UTF-8"), username) |
1681 | self.assertEqual([self.authserver.valid_user], self.authserver.calls) |
1682 | |
1683 | def test_noSuchUser_with_two_keys_calls_authserver_once(self): |
1684 | @@ -643,21 +704,22 @@ class TestPublicKeyFromLaunchpadChecker(testtools.TestCase): |
1685 | checker = self.makeChecker() |
1686 | mind = auth.UserDetailsMind() |
1687 | creds_1 = self.makeCredentials( |
1688 | - b'invalid-user', b'ssh-dss', b'invalid key 1', mind) |
1689 | + b"invalid-user", b"ssh-dss", b"invalid key 1", mind |
1690 | + ) |
1691 | creds_2 = self.makeCredentials( |
1692 | - b'invalid-user', b'ssh-dss', b'invalid key 2', mind) |
1693 | + b"invalid-user", b"ssh-dss", b"invalid key 2", mind |
1694 | + ) |
1695 | d = checker.requestAvatarId(creds_1) |
1696 | |
1697 | def try_second_key(failure): |
1698 | return assert_fails_with( |
1699 | - checker.requestAvatarId(creds_2), |
1700 | - UnauthorizedLogin) |
1701 | + checker.requestAvatarId(creds_2), UnauthorizedLogin |
1702 | + ) |
1703 | |
1704 | d.addErrback(try_second_key) |
1705 | |
1706 | def check_one_call(r): |
1707 | - self.assertEqual( |
1708 | - ['invalid-user'], self.authserver.calls) |
1709 | + self.assertEqual(["invalid-user"], self.authserver.calls) |
1710 | return r |
1711 | |
1712 | d.addCallback(check_one_call) |
1713 | diff --git a/src/lazr/sshserver/tests/test_docs.py b/src/lazr/sshserver/tests/test_docs.py |
1714 | index a331bc7..c498504 100644 |
1715 | --- a/src/lazr/sshserver/tests/test_docs.py |
1716 | +++ b/src/lazr/sshserver/tests/test_docs.py |
1717 | @@ -21,39 +21,48 @@ from __future__ import absolute_import, print_function |
1718 | |
1719 | __metaclass__ = type |
1720 | __all__ = [ |
1721 | - 'load_tests', |
1722 | - ] |
1723 | + "load_tests", |
1724 | +] |
1725 | |
1726 | import __future__ |
1727 | + |
1728 | import atexit |
1729 | import doctest |
1730 | import os |
1731 | + |
1732 | # pylint: disable-msg=F0401 |
1733 | from pkg_resources import ( |
1734 | - resource_filename, resource_exists, resource_listdir, cleanup_resources) |
1735 | - |
1736 | + cleanup_resources, |
1737 | + resource_exists, |
1738 | + resource_filename, |
1739 | + resource_listdir, |
1740 | +) |
1741 | |
1742 | DOCTEST_FLAGS = ( |
1743 | - doctest.ELLIPSIS | |
1744 | - doctest.NORMALIZE_WHITESPACE | |
1745 | - doctest.REPORT_NDIFF) |
1746 | + doctest.ELLIPSIS | doctest.NORMALIZE_WHITESPACE | doctest.REPORT_NDIFF |
1747 | +) |
1748 | |
1749 | |
1750 | def load_tests(loader, tests, pattern): |
1751 | """Load the doc tests (README.txt and docs/*, if any exist).""" |
1752 | doctest_files = [ |
1753 | - os.path.abspath(resource_filename('lazr.sshserver', 'README.txt'))] |
1754 | - if resource_exists('lazr.sshserver', 'docs'): |
1755 | - for name in resource_listdir('lazr.sshserver', 'docs'): |
1756 | - if name.endswith('.txt'): |
1757 | + os.path.abspath(resource_filename("lazr.sshserver", "README.txt")) |
1758 | + ] |
1759 | + if resource_exists("lazr.sshserver", "docs"): |
1760 | + for name in resource_listdir("lazr.sshserver", "docs"): |
1761 | + if name.endswith(".txt"): |
1762 | doctest_files.append( |
1763 | os.path.abspath( |
1764 | - resource_filename('lazr.sshserver', 'docs/%s' % name))) |
1765 | + resource_filename("lazr.sshserver", "docs/%s" % name) |
1766 | + ) |
1767 | + ) |
1768 | globs = { |
1769 | future_item: getattr(__future__, future_item) |
1770 | - for future_item in ('absolute_import', 'print_function')} |
1771 | + for future_item in ("absolute_import", "print_function") |
1772 | + } |
1773 | kwargs = dict( |
1774 | - module_relative=False, globs=globs, optionflags=DOCTEST_FLAGS) |
1775 | + module_relative=False, globs=globs, optionflags=DOCTEST_FLAGS |
1776 | + ) |
1777 | atexit.register(cleanup_resources) |
1778 | tests.addTest(doctest.DocFileSuite(*doctest_files, **kwargs)) |
1779 | return tests |
1780 | diff --git a/src/lazr/sshserver/tests/test_events.py b/src/lazr/sshserver/tests/test_events.py |
1781 | index f5fb161..a3e790a 100644 |
1782 | --- a/src/lazr/sshserver/tests/test_events.py |
1783 | +++ b/src/lazr/sshserver/tests/test_events.py |
1784 | @@ -10,19 +10,13 @@ __metaclass__ = type |
1785 | import logging |
1786 | |
1787 | import testtools |
1788 | -from zope.component import ( |
1789 | - adapter, |
1790 | - getGlobalSiteManager, |
1791 | - provideHandler, |
1792 | - ) |
1793 | + |
1794 | # This non-standard import is necessary to hook up the event system. |
1795 | import zope.component.event # noqa: F401 |
1796 | +from zope.component import adapter, getGlobalSiteManager, provideHandler |
1797 | from zope.event import notify |
1798 | |
1799 | -from lazr.sshserver.events import ( |
1800 | - ILoggingEvent, |
1801 | - LoggingEvent, |
1802 | - ) |
1803 | +from lazr.sshserver.events import ILoggingEvent, LoggingEvent |
1804 | |
1805 | |
1806 | class ListHandler(logging.Handler): |
1807 | @@ -48,7 +42,6 @@ class ListHandler(logging.Handler): |
1808 | |
1809 | |
1810 | class TestLoggingEvent(testtools.TestCase): |
1811 | - |
1812 | def assertLogs(self, records, function, *args, **kwargs): |
1813 | """Assert 'function' logs 'records' when run with the given args.""" |
1814 | logged_events = [] |
1815 | @@ -57,8 +50,12 @@ class TestLoggingEvent(testtools.TestCase): |
1816 | result = function(*args, **kwargs) |
1817 | self.logger.removeHandler(handler) |
1818 | self.assertEqual( |
1819 | - [(record.levelno, record.getMessage()) |
1820 | - for record in logged_events], records) |
1821 | + [ |
1822 | + (record.levelno, record.getMessage()) |
1823 | + for record in logged_events |
1824 | + ], |
1825 | + records, |
1826 | + ) |
1827 | return result |
1828 | |
1829 | def assertEventLogs(self, record, logging_event): |
1830 | @@ -79,15 +76,17 @@ class TestLoggingEvent(testtools.TestCase): |
1831 | |
1832 | def test_level(self): |
1833 | event = LoggingEvent(logging.CRITICAL, "foo") |
1834 | - self.assertEventLogs((logging.CRITICAL, 'foo'), event) |
1835 | + self.assertEventLogs((logging.CRITICAL, "foo"), event) |
1836 | |
1837 | def test_formatting(self): |
1838 | event = LoggingEvent(logging.DEBUG, "foo: %(name)s", name="bar") |
1839 | - self.assertEventLogs((logging.DEBUG, 'foo: bar'), event) |
1840 | + self.assertEventLogs((logging.DEBUG, "foo: bar"), event) |
1841 | |
1842 | def test_subclass(self): |
1843 | class SomeEvent(LoggingEvent): |
1844 | template = "%(something)s happened." |
1845 | level = logging.INFO |
1846 | + |
1847 | self.assertEventLogs( |
1848 | - (logging.INFO, 'foo happened.'), SomeEvent(something='foo')) |
1849 | + (logging.INFO, "foo happened."), SomeEvent(something="foo") |
1850 | + ) |
1851 | diff --git a/src/lazr/sshserver/tests/test_session.py b/src/lazr/sshserver/tests/test_session.py |
1852 | index 13a4667..aebc200 100644 |
1853 | --- a/src/lazr/sshserver/tests/test_session.py |
1854 | +++ b/src/lazr/sshserver/tests/test_session.py |
1855 | @@ -21,7 +21,7 @@ class MockSSHSession: |
1856 | self.log = log |
1857 | |
1858 | def writeExtended(self, channel, data): |
1859 | - self.log.append(('writeExtended', channel, data)) |
1860 | + self.log.append(("writeExtended", channel, data)) |
1861 | |
1862 | |
1863 | class MockProcessTransport: |
1864 | @@ -35,16 +35,16 @@ class MockProcessTransport: |
1865 | self.session = MockSSHSession(self.log) |
1866 | |
1867 | def closeStdin(self): |
1868 | - self.log.append(('closeStdin',)) |
1869 | + self.log.append(("closeStdin",)) |
1870 | |
1871 | def loseConnection(self): |
1872 | - self.log.append(('loseConnection',)) |
1873 | + self.log.append(("loseConnection",)) |
1874 | |
1875 | def signalProcess(self, signal): |
1876 | - self.log.append(('signalProcess', signal)) |
1877 | + self.log.append(("signalProcess", signal)) |
1878 | |
1879 | def write(self, data): |
1880 | - self.log.append(('write', data)) |
1881 | + self.log.append(("write", data)) |
1882 | |
1883 | |
1884 | class TestDoNothing(testtools.TestCase): |
1885 | @@ -62,24 +62,33 @@ class TestDoNothing(testtools.TestCase): |
1886 | |
1887 | def test_openShellNotImplemented(self): |
1888 | # openShell closes the connection. |
1889 | - protocol = MockProcessTransport('bash') |
1890 | + protocol = MockProcessTransport("bash") |
1891 | self.session.openShell(protocol) |
1892 | self.assertEqual( |
1893 | - [('writeExtended', connection.EXTENDED_DATA_STDERR, |
1894 | - 'No shells on this server.\r\n'), |
1895 | - ('loseConnection',)], |
1896 | - protocol.log) |
1897 | + [ |
1898 | + ( |
1899 | + "writeExtended", |
1900 | + connection.EXTENDED_DATA_STDERR, |
1901 | + "No shells on this server.\r\n", |
1902 | + ), |
1903 | + ("loseConnection",), |
1904 | + ], |
1905 | + protocol.log, |
1906 | + ) |
1907 | |
1908 | def test_windowChangedNotImplemented(self): |
1909 | # windowChanged raises a NotImplementedError. It doesn't matter what |
1910 | # we pass it. |
1911 | - self.assertRaises(NotImplementedError, |
1912 | - self.session.windowChanged, None) |
1913 | + self.assertRaises( |
1914 | + NotImplementedError, self.session.windowChanged, None |
1915 | + ) |
1916 | |
1917 | def test_providesISession(self): |
1918 | # DoNothingSession must provide ISession. |
1919 | - self.assertTrue(ISession.providedBy(self.session), |
1920 | - "DoNothingSession doesn't implement ISession") |
1921 | + self.assertTrue( |
1922 | + ISession.providedBy(self.session), |
1923 | + "DoNothingSession doesn't implement ISession", |
1924 | + ) |
1925 | |
1926 | def test_closedDoesNothing(self): |
1927 | # closed is a no-op. |
1928 | @@ -87,14 +96,20 @@ class TestDoNothing(testtools.TestCase): |
1929 | |
1930 | def test_execCommandNotImplemented(self): |
1931 | # DoNothingSession.execCommand spawns the appropriate process. |
1932 | - protocol = MockProcessTransport('bash') |
1933 | - command = 'cat /etc/hostname' |
1934 | + protocol = MockProcessTransport("bash") |
1935 | + command = "cat /etc/hostname" |
1936 | self.session.execCommand(protocol, command) |
1937 | self.assertEqual( |
1938 | - [('writeExtended', connection.EXTENDED_DATA_STDERR, |
1939 | - 'Not allowed to execute commands on this server.\r\n'), |
1940 | - ('loseConnection',)], |
1941 | - protocol.log) |
1942 | + [ |
1943 | + ( |
1944 | + "writeExtended", |
1945 | + connection.EXTENDED_DATA_STDERR, |
1946 | + "Not allowed to execute commands on this server.\r\n", |
1947 | + ), |
1948 | + ("loseConnection",), |
1949 | + ], |
1950 | + protocol.log, |
1951 | + ) |
1952 | |
1953 | def test_eofReceivedDoesNothingWhenNoCommand(self): |
1954 | # When no process has been created, 'eofReceived' is a no-op. |
1955 | diff --git a/tox.ini b/tox.ini |
1956 | index cc1721e..a7a7a95 100644 |
1957 | --- a/tox.ini |
1958 | +++ b/tox.ini |
1959 | @@ -1,5 +1,5 @@ |
1960 | [tox] |
1961 | -envlist = |
1962 | +envlist = |
1963 | py27,py35,py36,py37,py38,py39,py310 |
1964 | |
1965 | [testenv] |