183 lines
		
	
	
		
			6.0 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			183 lines
		
	
	
		
			6.0 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| # Copyright 2013-2016 DataStax, Inc.
 | |
| #
 | |
| # Licensed under the Apache License, Version 2.0 (the "License");
 | |
| # you may not use this file except in compliance with the License.
 | |
| # You may obtain a copy of the License at
 | |
| #
 | |
| # http://www.apache.org/licenses/LICENSE-2.0
 | |
| #
 | |
| # Unless required by applicable law or agreed to in writing, software
 | |
| # distributed under the License is distributed on an "AS IS" BASIS,
 | |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | |
| # See the License for the specific language governing permissions and
 | |
| try:
 | |
|     from puresasl.client import SASLClient
 | |
| except ImportError:
 | |
|     SASLClient = None
 | |
| 
 | |
| class AuthProvider(object):
 | |
|     """
 | |
|     An abstract class that defines the interface that will be used for
 | |
|     creating :class:`~.Authenticator` instances when opening new
 | |
|     connections to Cassandra.
 | |
| 
 | |
|     .. versionadded:: 2.0.0
 | |
|     """
 | |
| 
 | |
|     def new_authenticator(self, host):
 | |
|         """
 | |
|         Implementations of this class should return a new instance
 | |
|         of :class:`~.Authenticator` or one of its subclasses.
 | |
|         """
 | |
|         raise NotImplementedError()
 | |
| 
 | |
| 
 | |
| class Authenticator(object):
 | |
|     """
 | |
|     An abstract class that handles SASL authentication with Cassandra servers.
 | |
| 
 | |
|     Each time a new connection is created and the server requires authentication,
 | |
|     a new instance of this class will be created by the corresponding
 | |
|     :class:`~.AuthProvider` to handler that authentication. The lifecycle of the
 | |
|     new :class:`~.Authenticator` will the be:
 | |
| 
 | |
|     1) The :meth:`~.initial_response()` method will be called. The return
 | |
|     value will be sent to the server to initiate the handshake.
 | |
| 
 | |
|     2) The server will respond to each client response by either issuing a
 | |
|     challenge or indicating that the authentication is complete (successful or not).
 | |
|     If a new challenge is issued, :meth:`~.evaluate_challenge()`
 | |
|     will be called to produce a response that will be sent to the
 | |
|     server. This challenge/response negotiation will continue until the server
 | |
|     responds that authentication is successful (or an :exc:`~.AuthenticationFailed`
 | |
|     is raised).
 | |
| 
 | |
|     3) When the server indicates that authentication is successful,
 | |
|     :meth:`~.on_authentication_success` will be called a token string that
 | |
|     that the server may optionally have sent.
 | |
| 
 | |
|     The exact nature of the negotiation between the client and server is specific
 | |
|     to the authentication mechanism configured server-side.
 | |
| 
 | |
|     .. versionadded:: 2.0.0
 | |
|     """
 | |
| 
 | |
|     server_authenticator_class = None
 | |
|     """ Set during the connection AUTHENTICATE phase """
 | |
| 
 | |
|     def initial_response(self):
 | |
|         """
 | |
|         Returns an message to send to the server to initiate the SASL handshake.
 | |
|         :const:`None` may be returned to send an empty message.
 | |
|         """
 | |
|         return None
 | |
| 
 | |
|     def evaluate_challenge(self, challenge):
 | |
|         """
 | |
|         Called when the server sends a challenge message.  Generally, this method
 | |
|         should return :const:`None` when authentication is complete from a
 | |
|         client perspective.  Otherwise, a string should be returned.
 | |
|         """
 | |
|         raise NotImplementedError()
 | |
| 
 | |
|     def on_authentication_success(self, token):
 | |
|         """
 | |
|         Called when the server indicates that authentication was successful.
 | |
|         Depending on the authentication mechanism, `token` may be :const:`None`
 | |
|         or a string.
 | |
|         """
 | |
|         pass
 | |
| 
 | |
| 
 | |
| class PlainTextAuthProvider(AuthProvider):
 | |
|     """
 | |
|     An :class:`~.AuthProvider` that works with Cassandra's PasswordAuthenticator.
 | |
| 
 | |
|     Example usage::
 | |
| 
 | |
|         from cassandra.cluster import Cluster
 | |
|         from cassandra.auth import PlainTextAuthProvider
 | |
| 
 | |
|         auth_provider = PlainTextAuthProvider(
 | |
|                 username='cassandra', password='cassandra')
 | |
|         cluster = Cluster(auth_provider=auth_provider)
 | |
| 
 | |
|     .. versionadded:: 2.0.0
 | |
|     """
 | |
| 
 | |
|     def __init__(self, username, password):
 | |
|         self.username = username
 | |
|         self.password = password
 | |
| 
 | |
|     def new_authenticator(self, host):
 | |
|         return PlainTextAuthenticator(self.username, self.password)
 | |
| 
 | |
| 
 | |
| class PlainTextAuthenticator(Authenticator):
 | |
|     """
 | |
|     An :class:`~.Authenticator` that works with Cassandra's PasswordAuthenticator.
 | |
| 
 | |
|     .. versionadded:: 2.0.0
 | |
|     """
 | |
| 
 | |
|     def __init__(self, username, password):
 | |
|         self.username = username
 | |
|         self.password = password
 | |
| 
 | |
|     def initial_response(self):
 | |
|         return "\x00%s\x00%s" % (self.username, self.password)
 | |
| 
 | |
|     def evaluate_challenge(self, challenge):
 | |
|         return None
 | |
| 
 | |
| 
 | |
| class SaslAuthProvider(AuthProvider):
 | |
|     """
 | |
|     An :class:`~.AuthProvider` supporting general SASL auth mechanisms
 | |
| 
 | |
|     Suitable for GSSAPI or other SASL mechanisms
 | |
| 
 | |
|     Example usage::
 | |
| 
 | |
|         from cassandra.cluster import Cluster
 | |
|         from cassandra.auth import SaslAuthProvider
 | |
| 
 | |
|         sasl_kwargs = {'service': 'something',
 | |
|                        'mechanism': 'GSSAPI',
 | |
|                        'qops': 'auth'.split(',')}
 | |
|         auth_provider = SaslAuthProvider(**sasl_kwargs)
 | |
|         cluster = Cluster(auth_provider=auth_provider)
 | |
| 
 | |
|     .. versionadded:: 2.1.4
 | |
|     """
 | |
| 
 | |
|     def __init__(self, **sasl_kwargs):
 | |
|         if SASLClient is None:
 | |
|             raise ImportError('The puresasl library has not been installed')
 | |
|         if 'host' in sasl_kwargs:
 | |
|             raise ValueError("kwargs should not contain 'host' since it is passed dynamically to new_authenticator")
 | |
|         self.sasl_kwargs = sasl_kwargs
 | |
| 
 | |
|     def new_authenticator(self, host):
 | |
|         return SaslAuthenticator(host, **self.sasl_kwargs)
 | |
| 
 | |
| 
 | |
| class SaslAuthenticator(Authenticator):
 | |
|     """
 | |
|     A pass-through :class:`~.Authenticator` using the third party package
 | |
|     'pure-sasl' for authentication
 | |
| 
 | |
|     .. versionadded:: 2.1.4
 | |
|     """
 | |
| 
 | |
|     def __init__(self, host, service, mechanism='GSSAPI', **sasl_kwargs):
 | |
|         if SASLClient is None:
 | |
|             raise ImportError('The puresasl library has not been installed')
 | |
|         self.sasl = SASLClient(host, service, mechanism, **sasl_kwargs)
 | |
| 
 | |
|     def initial_response(self):
 | |
|         return self.sasl.process()
 | |
| 
 | |
|     def evaluate_challenge(self, challenge):
 | |
|         return self.sasl.process(challenge)
 | 
