Skip to content

Commit

Permalink
fix twist conch --auth=sshkey
Browse files Browse the repository at this point in the history
  • Loading branch information
glyph committed Feb 14, 2023
1 parent a60a32b commit 554f874
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 11 deletions.
19 changes: 14 additions & 5 deletions src/twisted/conch/unix.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
A UNIX SSH server.
"""

from __future__ import annotations

import fcntl
import grp
import os
Expand All @@ -14,8 +16,10 @@
import struct
import time
import tty
from typing import Callable, Dict, Tuple

from zope.interface import implementer
from zope.interface.interfaces import IInterface

from twisted.conch import ttymodes
from twisted.conch.avatar import ConchUser
Expand All @@ -33,6 +37,7 @@
)
from twisted.cred import portal
from twisted.internet.error import ProcessExitedAlready
from twisted.internet.interfaces import IListeningPort
from twisted.logger import Logger
from twisted.python import components
from twisted.python.compat import nativeString
Expand All @@ -45,13 +50,15 @@

@implementer(portal.IRealm)
class UnixSSHRealm:
def requestAvatar(self, username, mind, *interfaces):
user = UnixConchUser(username)
def requestAvatar(
self, username: bytes, mind: object, *interfaces: IInterface
) -> Tuple[IInterface, UnixConchUser, Callable[[], None]]:
user = UnixConchUser(username.decode())
return interfaces[0], user, user.logout


class UnixConchUser(ConchUser):
def __init__(self, username):
def __init__(self, username: str) -> None:
ConchUser.__init__(self)
self.username = username
self.pwdData = pwd.getpwnam(self.username)
Expand All @@ -60,7 +67,9 @@ def __init__(self, username):
if username in userlist:
l.append(gid)
self.otherGroups = l
self.listeners = {} # Dict mapping (interface, port) -> listener
self.listeners: Dict[
str, IListeningPort
] = {} # Dict mapping (interface, port) -> listener
self.channelLookup.update(
{
b"session": session.SSHSession,
Expand Down Expand Up @@ -116,7 +125,7 @@ def global_cancel_tcpip_forward(self, data):
self._runAsUser(listener.stopListening)
return 1

def logout(self):
def logout(self) -> None:
# Remove all listeners.
for listener in self.listeners.values():
self._runAsUser(listener.stopListening)
Expand Down
29 changes: 23 additions & 6 deletions src/twisted/cred/portal.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,30 @@
"""


from typing import Callable, Dict, Iterable, List, Tuple, Union

from zope.interface import Interface, providedBy
from zope.interface.interfaces import IInterface

from twisted.cred import error
from twisted.cred.checkers import ICredentialsChecker
from twisted.cred.credentials import ICredentials
from twisted.internet import defer
from twisted.internet.defer import maybeDeferred
from twisted.internet.defer import Deferred, maybeDeferred
from twisted.python import failure, reflect

_requestResult = Tuple[IInterface, object, Callable[[], None]]


class IRealm(Interface):
"""
The realm connects application-specific objects to the
authentication system.
"""

def requestAvatar(avatarId, mind, *interfaces):
def requestAvatar(
avatarId: bytes, mind: object, *interfaces: IInterface
) -> Union[Deferred[_requestResult], _requestResult]:
"""
Return avatar which provides one of the given interfaces.
Expand Down Expand Up @@ -57,7 +66,11 @@ class Portal:
in the realm object and in the credentials checker objects.
"""

def __init__(self, realm, checkers=()):
checkers: Dict[IInterface, ICredentialsChecker]

def __init__(
self, realm: IRealm, checkers: Iterable[ICredentialsChecker] = ()
) -> None:
"""
Create a Portal to a L{IRealm}.
"""
Expand All @@ -66,19 +79,23 @@ def __init__(self, realm, checkers=()):
for checker in checkers:
self.registerChecker(checker)

def listCredentialsInterfaces(self):
def listCredentialsInterfaces(self) -> List[Interface]:
"""
Return list of credentials interfaces that can be used to login.
"""
return list(self.checkers.keys())

def registerChecker(self, checker, *credentialInterfaces):
def registerChecker(
self, checker: ICredentialsChecker, *credentialInterfaces: IInterface
) -> None:
if not credentialInterfaces:
credentialInterfaces = checker.credentialInterfaces
for credentialInterface in credentialInterfaces:
self.checkers[credentialInterface] = checker

def login(self, credentials, mind, *interfaces):
def login(
self, credentials: ICredentials, mind: object, *interfaces: IInterface
) -> Deferred[_requestResult]:
"""
@param credentials: an implementor of
L{twisted.cred.credentials.ICredentials}
Expand Down
1 change: 1 addition & 0 deletions src/twisted/newsfragments/11626.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
`twist conch --auth=sshkey` can now authenticate users without a traceback again, thanks to twisted.conch.unix.UnixConchUser no longer being incorrectly instantiated with `bytes`. In the course of this fix, some type hinting has also been applied to `twisted.cred.portal`.

0 comments on commit 554f874

Please sign in to comment.