Updates
This commit is contained in:
191
ETB-API/venv/lib/python3.12/site-packages/amqp/sasl.py
Normal file
191
ETB-API/venv/lib/python3.12/site-packages/amqp/sasl.py
Normal file
@@ -0,0 +1,191 @@
|
||||
"""SASL mechanisms for AMQP authentication."""
|
||||
|
||||
import socket
|
||||
import warnings
|
||||
from io import BytesIO
|
||||
|
||||
from amqp.serialization import _write_table
|
||||
|
||||
|
||||
class SASL:
|
||||
"""The base class for all amqp SASL authentication mechanisms.
|
||||
|
||||
You should sub-class this if you're implementing your own authentication.
|
||||
"""
|
||||
|
||||
@property
|
||||
def mechanism(self):
|
||||
"""Return a bytes containing the SASL mechanism name."""
|
||||
raise NotImplementedError
|
||||
|
||||
def start(self, connection):
|
||||
"""Return the first response to a SASL challenge as a bytes object."""
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class PLAIN(SASL):
|
||||
"""PLAIN SASL authentication mechanism.
|
||||
|
||||
See https://tools.ietf.org/html/rfc4616 for details
|
||||
"""
|
||||
|
||||
mechanism = b'PLAIN'
|
||||
|
||||
def __init__(self, username, password):
|
||||
self.username, self.password = username, password
|
||||
|
||||
__slots__ = (
|
||||
"username",
|
||||
"password",
|
||||
)
|
||||
|
||||
def start(self, connection):
|
||||
if self.username is None or self.password is None:
|
||||
return NotImplemented
|
||||
login_response = BytesIO()
|
||||
login_response.write(b'\0')
|
||||
login_response.write(self.username.encode('utf-8'))
|
||||
login_response.write(b'\0')
|
||||
login_response.write(self.password.encode('utf-8'))
|
||||
return login_response.getvalue()
|
||||
|
||||
|
||||
class AMQPLAIN(SASL):
|
||||
"""AMQPLAIN SASL authentication mechanism.
|
||||
|
||||
This is a non-standard mechanism used by AMQP servers.
|
||||
"""
|
||||
|
||||
mechanism = b'AMQPLAIN'
|
||||
|
||||
def __init__(self, username, password):
|
||||
self.username, self.password = username, password
|
||||
|
||||
__slots__ = (
|
||||
"username",
|
||||
"password",
|
||||
)
|
||||
|
||||
def start(self, connection):
|
||||
if self.username is None or self.password is None:
|
||||
return NotImplemented
|
||||
login_response = BytesIO()
|
||||
_write_table({b'LOGIN': self.username, b'PASSWORD': self.password},
|
||||
login_response.write, [])
|
||||
# Skip the length at the beginning
|
||||
return login_response.getvalue()[4:]
|
||||
|
||||
|
||||
def _get_gssapi_mechanism():
|
||||
try:
|
||||
import gssapi
|
||||
import gssapi.raw.misc # Fail if the old python-gssapi is installed
|
||||
except ImportError:
|
||||
class FakeGSSAPI(SASL):
|
||||
"""A no-op SASL mechanism for when gssapi isn't available."""
|
||||
|
||||
mechanism = None
|
||||
|
||||
def __init__(self, client_name=None, service=b'amqp',
|
||||
rdns=False, fail_soft=False):
|
||||
if not fail_soft:
|
||||
raise NotImplementedError(
|
||||
"You need to install the `gssapi` module for GSSAPI "
|
||||
"SASL support")
|
||||
|
||||
def start(self): # pragma: no cover
|
||||
return NotImplemented
|
||||
return FakeGSSAPI
|
||||
else:
|
||||
class GSSAPI(SASL):
|
||||
"""GSSAPI SASL authentication mechanism.
|
||||
|
||||
See https://tools.ietf.org/html/rfc4752 for details
|
||||
"""
|
||||
|
||||
mechanism = b'GSSAPI'
|
||||
|
||||
def __init__(self, client_name=None, service=b'amqp',
|
||||
rdns=False, fail_soft=False):
|
||||
if client_name and not isinstance(client_name, bytes):
|
||||
client_name = client_name.encode('ascii')
|
||||
self.client_name = client_name
|
||||
self.fail_soft = fail_soft
|
||||
self.service = service
|
||||
self.rdns = rdns
|
||||
|
||||
__slots__ = (
|
||||
"client_name",
|
||||
"fail_soft",
|
||||
"service",
|
||||
"rdns"
|
||||
)
|
||||
|
||||
def get_hostname(self, connection):
|
||||
sock = connection.transport.sock
|
||||
if self.rdns and sock.family in (socket.AF_INET,
|
||||
socket.AF_INET6):
|
||||
peer = sock.getpeername()
|
||||
hostname, _, _ = socket.gethostbyaddr(peer[0])
|
||||
else:
|
||||
hostname = connection.transport.host
|
||||
if not isinstance(hostname, bytes):
|
||||
hostname = hostname.encode('ascii')
|
||||
return hostname
|
||||
|
||||
def start(self, connection):
|
||||
try:
|
||||
if self.client_name:
|
||||
creds = gssapi.Credentials(
|
||||
name=gssapi.Name(self.client_name))
|
||||
else:
|
||||
creds = None
|
||||
hostname = self.get_hostname(connection)
|
||||
name = gssapi.Name(b'@'.join([self.service, hostname]),
|
||||
gssapi.NameType.hostbased_service)
|
||||
context = gssapi.SecurityContext(name=name, creds=creds)
|
||||
return context.step(None)
|
||||
except gssapi.raw.misc.GSSError:
|
||||
if self.fail_soft:
|
||||
return NotImplemented
|
||||
else:
|
||||
raise
|
||||
return GSSAPI
|
||||
|
||||
|
||||
GSSAPI = _get_gssapi_mechanism()
|
||||
|
||||
|
||||
class EXTERNAL(SASL):
|
||||
"""EXTERNAL SASL mechanism.
|
||||
|
||||
Enables external authentication, i.e. not handled through this protocol.
|
||||
Only passes 'EXTERNAL' as authentication mechanism, but no further
|
||||
authentication data.
|
||||
"""
|
||||
|
||||
mechanism = b'EXTERNAL'
|
||||
|
||||
def start(self, connection):
|
||||
return b''
|
||||
|
||||
|
||||
class RAW(SASL):
|
||||
"""A generic custom SASL mechanism.
|
||||
|
||||
This mechanism takes a mechanism name and response to send to the server,
|
||||
so can be used for simple custom authentication schemes.
|
||||
"""
|
||||
|
||||
mechanism = None
|
||||
|
||||
def __init__(self, mechanism, response):
|
||||
assert isinstance(mechanism, bytes)
|
||||
assert isinstance(response, bytes)
|
||||
self.mechanism, self.response = mechanism, response
|
||||
warnings.warn("Passing login_method and login_response to Connection "
|
||||
"is deprecated. Please implement a SASL subclass "
|
||||
"instead.", DeprecationWarning)
|
||||
|
||||
def start(self, connection):
|
||||
return self.response
|
||||
Reference in New Issue
Block a user