summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--README.rst2
-rw-r--r--paramiko/__init__.py2
-rw-r--r--paramiko/auth_handler.py4
-rw-r--r--paramiko/client.py6
-rw-r--r--paramiko/common.py31
-rw-r--r--paramiko/py3compat.py20
-rw-r--r--paramiko/sftp_client.py4
-rw-r--r--paramiko/ssh_gss.py7
-rw-r--r--setup.py1
-rw-r--r--sites/www/changelog.rst16
-rw-r--r--sites/www/installing-1.x.rst2
-rw-r--r--sites/www/installing.rst10
-rw-r--r--tests/test_auth.py18
13 files changed, 76 insertions, 47 deletions
diff --git a/README.rst b/README.rst
index 6e49bd68..ab383459 100644
--- a/README.rst
+++ b/README.rst
@@ -11,7 +11,7 @@ Paramiko
:Paramiko: Python SSH module
:Copyright: Copyright (c) 2003-2009 Robey Pointer <robeypointer@gmail.com>
-:Copyright: Copyright (c) 2013-2017 Jeff Forcier <jeff@bitprophet.org>
+:Copyright: Copyright (c) 2013-2018 Jeff Forcier <jeff@bitprophet.org>
:License: `LGPL <https://www.gnu.org/copyleft/lesser.html>`_
:Homepage: http://www.paramiko.org/
:API docs: http://docs.paramiko.org
diff --git a/paramiko/__init__.py b/paramiko/__init__.py
index c4c69a45..4d80bf26 100644
--- a/paramiko/__init__.py
+++ b/paramiko/__init__.py
@@ -84,6 +84,8 @@ __all__ = [
'PKey',
'RSAKey',
'DSSKey',
+ 'ECDSAKey',
+ 'Ed25519Key',
'Message',
'SSHException',
'AuthenticationException',
diff --git a/paramiko/auth_handler.py b/paramiko/auth_handler.py
index a1ce5e3b..3b894de7 100644
--- a/paramiko/auth_handler.py
+++ b/paramiko/auth_handler.py
@@ -39,7 +39,7 @@ from paramiko.common import (
cMSG_USERAUTH_BANNER
)
from paramiko.message import Message
-from paramiko.py3compat import bytestring
+from paramiko.py3compat import b
from paramiko.ssh_exception import (
SSHException, AuthenticationException, BadAuthenticationType,
PartialAuthentication,
@@ -256,7 +256,7 @@ class AuthHandler (object):
m.add_string(self.auth_method)
if self.auth_method == 'password':
m.add_boolean(False)
- password = bytestring(self.password)
+ password = b(self.password)
m.add_string(password)
elif self.auth_method == 'publickey':
m.add_boolean(True)
diff --git a/paramiko/client.py b/paramiko/client.py
index 6f0cb847..de0a495e 100644
--- a/paramiko/client.py
+++ b/paramiko/client.py
@@ -463,6 +463,9 @@ class SSHClient (ClosingContextManager):
Python
:param int timeout:
set command's channel timeout. See `.Channel.settimeout`
+ :param bool get_pty:
+ Request a pseudo-terminal from the server (default ``False``).
+ See `.Channel.get_pty`
:param dict environment:
a dict of shell environment variables, to be merged into the
default environment that the remote command executes within.
@@ -476,6 +479,9 @@ class SSHClient (ClosingContextManager):
3-tuple
:raises: `.SSHException` -- if the server fails to execute the command
+
+ .. versionchanged:: 1.10
+ Added the ``get_pty`` kwarg.
"""
chan = self._transport.open_session(timeout=timeout)
if get_pty:
diff --git a/paramiko/common.py b/paramiko/common.py
index 11c4121d..eab6647e 100644
--- a/paramiko/common.py
+++ b/paramiko/common.py
@@ -20,7 +20,7 @@
Common constants and global variables.
"""
import logging
-from paramiko.py3compat import byte_chr, PY2, bytes_types, text_type, long
+from paramiko.py3compat import byte_chr, PY2, long, b
MSG_DISCONNECT, MSG_IGNORE, MSG_UNIMPLEMENTED, MSG_DEBUG, \
MSG_SERVICE_REQUEST, MSG_SERVICE_ACCEPT = range(1, 7)
@@ -162,17 +162,24 @@ else:
def asbytes(s):
- """Coerce to bytes if possible or return unchanged."""
- if isinstance(s, bytes_types):
- return s
- if isinstance(s, text_type):
- # Accept text and encode as utf-8 for compatibility only.
- return s.encode("utf-8")
- asbytes = getattr(s, "asbytes", None)
- if asbytes is not None:
- return asbytes()
- # May be an object that implements the buffer api, let callers handle.
- return s
+ """
+ Coerce to bytes if possible or return unchanged.
+ """
+ try:
+ # Attempt to run through our version of b(), which does the Right Thing
+ # for string/unicode/buffer (Py2) or bytes/str (Py3), and raises
+ # TypeError if it's not one of those types.
+ return b(s)
+ except TypeError:
+ try:
+ # If it wasn't a string/byte/buffer type object, try calling an
+ # asbytes() method, which many of our internal classes implement.
+ return s.asbytes()
+ except AttributeError:
+ # Finally, just do nothing & assume this object is sufficiently
+ # byte-y or buffer-y that everything will work out (or that callers
+ # are capable of handling whatever it is.)
+ return s
xffffffff = long(0xffffffff)
diff --git a/paramiko/py3compat.py b/paramiko/py3compat.py
index cb9de412..67c0f200 100644
--- a/paramiko/py3compat.py
+++ b/paramiko/py3compat.py
@@ -1,11 +1,12 @@
import sys
import base64
-__all__ = ['PY2', 'string_types', 'integer_types', 'text_type', 'bytes_types',
- 'bytes', 'long', 'input', 'decodebytes', 'encodebytes',
- 'bytestring', 'byte_ord', 'byte_chr', 'byte_mask', 'b', 'u', 'b2s',
- 'StringIO', 'BytesIO', 'is_callable', 'MAXSIZE',
- 'next', 'builtins']
+__all__ = [
+ 'BytesIO', 'MAXSIZE', 'PY2', 'StringIO', 'b', 'b2s', 'builtins',
+ 'byte_chr', 'byte_mask', 'byte_ord', 'bytes', 'bytes_types', 'decodebytes',
+ 'encodebytes', 'input', 'integer_types', 'is_callable', 'long', 'next',
+ 'string_types', 'text_type', 'u',
+]
PY2 = sys.version_info[0] < 3
@@ -23,12 +24,6 @@ if PY2:
import __builtin__ as builtins
- def bytestring(s): # NOQA
- if isinstance(s, unicode): # NOQA
- return s.encode('utf-8')
- return s
-
-
byte_ord = ord # NOQA
byte_chr = chr # NOQA
@@ -111,9 +106,6 @@ else:
decodebytes = base64.decodebytes
encodebytes = base64.encodebytes
- def bytestring(s):
- return s
-
def byte_ord(c):
# In case we're handed a string instead of an int.
if not isinstance(c, int):
diff --git a/paramiko/sftp_client.py b/paramiko/sftp_client.py
index b344dff3..31dc234c 100644
--- a/paramiko/sftp_client.py
+++ b/paramiko/sftp_client.py
@@ -28,7 +28,7 @@ from paramiko import util
from paramiko.channel import Channel
from paramiko.message import Message
from paramiko.common import INFO, DEBUG, o777
-from paramiko.py3compat import bytestring, b, u, long
+from paramiko.py3compat import b, u, long
from paramiko.sftp import (
BaseSFTP, CMD_OPENDIR, CMD_HANDLE, SFTPError, CMD_READDIR, CMD_NAME,
CMD_CLOSE, SFTP_FLAG_READ, SFTP_FLAG_WRITE, SFTP_FLAG_CREATE,
@@ -489,7 +489,7 @@ class SFTPClient(BaseSFTP, ClosingContextManager):
"""
dest = self._adjust_cwd(dest)
self._log(DEBUG, 'symlink({!r}, {!r})'.format(source, dest))
- source = bytestring(source)
+ source = b(source)
self._request(CMD_SYMLINK, source, dest)
def chmod(self, path, mode):
diff --git a/paramiko/ssh_gss.py b/paramiko/ssh_gss.py
index 88dedf7e..aa7cc74d 100644
--- a/paramiko/ssh_gss.py
+++ b/paramiko/ssh_gss.py
@@ -42,8 +42,6 @@ GSS_AUTH_AVAILABLE = True
GSS_EXCEPTIONS = ()
-from pyasn1.type.univ import ObjectIdentifier
-from pyasn1.codec.der import encoder, decoder
#: :var str _API: Constraint for the used API
@@ -160,6 +158,8 @@ class _SSH_GSSAuth(object):
:note: In server mode we just return the OID length and the DER encoded
OID.
"""
+ from pyasn1.type.univ import ObjectIdentifier
+ from pyasn1.codec.der import encoder
OIDs = self._make_uint32(1)
krb5_OID = encoder.encode(ObjectIdentifier(self._krb5_mech))
OID_len = self._make_uint32(len(krb5_OID))
@@ -174,6 +174,7 @@ class _SSH_GSSAuth(object):
:param str desired_mech: The desired GSS-API mechanism of the client
:return: ``True`` if the given OID is supported, otherwise C{False}
"""
+ from pyasn1.codec.der import decoder
mech, __ = decoder.decode(desired_mech)
if mech.__str__() != self._krb5_mech:
return False
@@ -260,6 +261,7 @@ class _SSH_GSSAPI(_SSH_GSSAuth):
:return: A ``String`` if the GSS-API has returned a token or
``None`` if no token was returned
"""
+ from pyasn1.codec.der import decoder
self._username = username
self._gss_host = target
targ_name = gssapi.Name("host@" + self._gss_host,
@@ -428,6 +430,7 @@ class _SSH_SSPI(_SSH_GSSAuth):
:return: A ``String`` if the SSPI has returned a token or ``None`` if
no token was returned
"""
+ from pyasn1.codec.der import decoder
self._username = username
self._gss_host = target
error = 0
diff --git a/setup.py b/setup.py
index 6e1f0e0e..01150831 100644
--- a/setup.py
+++ b/setup.py
@@ -75,6 +75,5 @@ setup(
'bcrypt>=3.1.3',
'cryptography>=1.5',
'pynacl>=1.0.1',
- 'pyasn1>=0.1.7',
],
)
diff --git a/sites/www/changelog.rst b/sites/www/changelog.rst
index b6343c87..3dcebe29 100644
--- a/sites/www/changelog.rst
+++ b/sites/www/changelog.rst
@@ -2,6 +2,18 @@
Changelog
=========
+* :support:`1191` Update our install docs with (somewhat) recently added
+ additional dependencies; we previously only required Cryptography, but the
+ docs never got updated after we incurred ``bcrypt`` and ``pynacl``
+ requirements for Ed25519 key support.
+
+ Additionally, ``pyasn1`` was never actually hard-required; it was necessary
+ during a development branch, and is used by the optional GSSAPI support, but
+ is not required for regular installation. Thus, it has been removed from our
+ ``setup.py`` and its imports in the GSSAPI code made optional.
+
+ Credit to ``@stevenwinfield`` for highlighting the outdated install docs.
+
* :release:`2.4.1 <2018-03-12>`
* :release:`2.3.2 <2018-03-12>`
* :release:`2.2.3 <2018-03-12>`
@@ -14,6 +26,10 @@ Changelog
where authentication status was not checked before processing channel-open
and other requests typically only sent after authenticating. Big thanks to
Matthijs Kooijman for the report.
+* :bug:`1168` Add newer key classes for Ed25519 and ECDSA to
+ ``paramiko.__all__`` so that code introspecting that attribute, or using
+ ``from paramiko import *`` (such as some IDEs) sees them. Thanks to
+ ``@patriksevallius`` for the patch.
* :bug:`1039` Ed25519 auth key decryption raised an unexpected exception when
given a unicode password string (typical in python 3). Report by Theodor van
Nahl and fix by Pierce Lopez.
diff --git a/sites/www/installing-1.x.rst b/sites/www/installing-1.x.rst
index 8ede40d5..7421a6c2 100644
--- a/sites/www/installing-1.x.rst
+++ b/sites/www/installing-1.x.rst
@@ -118,4 +118,4 @@ First, see the main install doc's notes: :ref:`gssapi` - everything there is
required for Paramiko 1.x as well.
Additionally, users of Paramiko 1.x, on all platforms, need a final dependency:
-`pyasn1 <https://pypi.python.org/pypi/pyasn1>`_ ``0.1.7`` or better.
+`pyasn1 <https://pypi.org/project/pyasn1/>`_ ``0.1.7`` or better.
diff --git a/sites/www/installing.rst b/sites/www/installing.rst
index e6db2dca..3631eb0d 100644
--- a/sites/www/installing.rst
+++ b/sites/www/installing.rst
@@ -22,8 +22,12 @@ via `pip <http://pip-installer.org>`_::
We currently support **Python 2.7, 3.4+, and PyPy**. Users on Python 2.6 or
older (or 3.3 or older) are urged to upgrade.
-Paramiko has only one direct hard dependency: the Cryptography library. See
-:ref:`cryptography`.
+Paramiko has only a few direct dependencies:
+
+- The big one, with its own sub-dependencies, is Cryptography; see :ref:`its
+ specific note below <cryptography>` for more details.
+- `bcrypt <https://pypi.org/project/bcrypt/>`_, for Ed25519 key support;
+- `pynacl <https://pypi.org/project/PyNaCl/>`_, also for Ed25519 key support.
If you need GSS-API / SSPI support, see :ref:`the below subsection on it
<gssapi>` for details on its optional dependencies.
@@ -97,7 +101,7 @@ due to their infrequent utility & non-platform-agnostic requirements):
* It hopefully goes without saying but **all platforms** need **a working
installation of GSS-API itself**, e.g. Heimdal.
-* **Unix** needs `python-gssapi <https://pypi.python.org/pypi/python-gssapi/>`_
+* **Unix** needs `python-gssapi <https://pypi.org/project/python-gssapi/>`_
``0.6.1`` or better.
.. note:: This library appears to only function on Python 2.7 and up.
diff --git a/tests/test_auth.py b/tests/test_auth.py
index 4eade610..dacdd654 100644
--- a/tests/test_auth.py
+++ b/tests/test_auth.py
@@ -133,7 +133,7 @@ class AuthTest (unittest.TestCase):
self.assertTrue(self.event.is_set())
self.assertTrue(self.ts.is_active())
- def test_1_bad_auth_type(self):
+ def test_bad_auth_type(self):
"""
verify that we get the right exception when an unsupported auth
type is requested.
@@ -148,7 +148,7 @@ class AuthTest (unittest.TestCase):
self.assertEqual(BadAuthenticationType, etype)
self.assertEqual(['publickey'], evalue.allowed_types)
- def test_2_bad_password(self):
+ def test_bad_password(self):
"""
verify that a bad password gets the right exception, and that a retry
with the right password works.
@@ -164,7 +164,7 @@ class AuthTest (unittest.TestCase):
self.tc.auth_password(username='slowdive', password='pygmalion')
self.verify_finished()
- def test_3_multipart_auth(self):
+ def test_multipart_auth(self):
"""
verify that multipart auth works.
"""
@@ -177,7 +177,7 @@ class AuthTest (unittest.TestCase):
self.assertEqual([], remain)
self.verify_finished()
- def test_4_interactive_auth(self):
+ def test_interactive_auth(self):
"""
verify keyboard-interactive auth works.
"""
@@ -195,7 +195,7 @@ class AuthTest (unittest.TestCase):
self.assertEqual([], remain)
self.verify_finished()
- def test_5_interactive_auth_fallback(self):
+ def test_interactive_auth_fallback(self):
"""
verify that a password auth attempt will fallback to "interactive"
if password auth isn't supported but interactive is.
@@ -206,7 +206,7 @@ class AuthTest (unittest.TestCase):
self.assertEqual([], remain)
self.verify_finished()
- def test_6_auth_utf8(self):
+ def test_auth_utf8(self):
"""
verify that utf-8 encoding happens in authentication.
"""
@@ -216,7 +216,7 @@ class AuthTest (unittest.TestCase):
self.assertEqual([], remain)
self.verify_finished()
- def test_7_auth_non_utf8(self):
+ def test_auth_non_utf8(self):
"""
verify that non-utf-8 encoded passwords can be used for broken
servers.
@@ -227,7 +227,7 @@ class AuthTest (unittest.TestCase):
self.assertEqual([], remain)
self.verify_finished()
- def test_8_auth_gets_disconnected(self):
+ def test_auth_gets_disconnected(self):
"""
verify that we catch a server disconnecting during auth, and report
it as an auth failure.
@@ -241,7 +241,7 @@ class AuthTest (unittest.TestCase):
self.assertTrue(issubclass(etype, AuthenticationException))
@slow
- def test_9_auth_non_responsive(self):
+ def test_auth_non_responsive(self):
"""
verify that authentication times out if server takes to long to
respond (or never responds).