def test_configure_with_cert_chain(): ''' Just checks that the certs don't error, not that they're actually loaded. TODO: improve. ''' context = tls.configure_tls_context( client_cert='tests/certs/selfsigned.crt', client_key='tests/certs/selfsigned.key') assert isinstance(context, ssl.SSLContext)
async def connect(self, **kwargs): ''' Open asyncio streams to the server and check response status. Accepts all of the same keyword arguments as __init__ (except loop). ''' for kwarg in kwargs.keys(): if kwarg not in self._connect_kwargs: raise TypeError( "connect() got an unexptected keyword argument " "'{}'".format(kwarg)) if kwargs.get('tls_context') and kwargs.get('client_cert'): raise ValueError( 'Either an SSLContext or a certificate/key must be provided') self._connect_kwargs.update(kwargs) if self._connect_kwargs['port'] is not None: port = self._connect_kwargs['port'] elif self._connect_kwargs['use_tls']: port = SMTP_TLS_PORT else: port = SMTP_PORT if self._connect_kwargs['use_tls']: if self._connect_kwargs['tls_context']: tls_context = self._connect_kwargs['tls_context'] else: tls_context = configure_tls_context( validate_certs=self._connect_kwargs['validate_certs'], client_cert=self._connect_kwargs['client_cert'], client_key=self._connect_kwargs['client_key']) else: tls_context = None self._source_address = self._connect_kwargs['source_address'] self._server_state = {} return await self._connect(self._connect_kwargs['hostname'], port, tls_context)
async def starttls(self, server_hostname=None, **kwargs): ''' Puts the connection to the SMTP server into TLS mode. If there has been no previous EHLO or HELO command this session, this method tries ESMTP EHLO first. If the server supports TLS, this will encrypt the rest of the SMTP session. If you provide the keyfile and certfile parameters, the identity of the SMTP server and client can be checked (if validate_certs is True). You can also provide a custom SSLContext object. If no certs or SSLContext is given, and TLS config was provided when initializing the class, STARTTLS will use to that, otherwise it will use the Python defaults. This method may raise the following exceptions: SMTPHeloError The server didn't reply properly to the helo greeting. ValueError An unsupported combination of args was provided. ''' allowed_kwargs = ( 'validate_certs', 'client_cert', 'client_key', 'tls_context', ) for kwarg in kwargs.keys(): if kwarg not in allowed_kwargs: raise TypeError( "connect() got an unexptected keyword argument " "'{}'".format(kwarg)) if kwargs.get('tls_context') and kwargs.get('client_cert'): raise ValueError( 'Either an SSLContext or a certificate/key must be provided') self._connect_kwargs.update(kwargs) if server_hostname is None: server_hostname = self._connect_kwargs['hostname'] if self._connect_kwargs['tls_context']: tls_context = self._connect_kwargs['tls_context'] else: tls_context = configure_tls_context( validate_certs=self._connect_kwargs['validate_certs'], client_cert=self._connect_kwargs['client_cert'], client_key=self._connect_kwargs['client_key']) await self.ehlo_or_helo_if_needed() if not self.supports_extension('starttls'): raise SMTPException( 'SMTP STARTTLS extension not supported by server.') response = await self.execute_command('STARTTLS') if response.code == status.SMTP_220_READY: self.protocol, self.transport = await self.writer.start_tls( tls_context, server_hostname=server_hostname) # RFC 3207 part 4.2: # The client MUST discard any knowledge obtained from # the server, such as the list of SMTP service extensions, # which was not obtained from the TLS negotiation itself. self._server_state = {} return response
def test_configure_tls_context_with_no_ssl_module_raises(monkeypatch): monkeypatch.setattr(tls, '_has_tls', False) with pytest.raises(RuntimeError): tls.configure_tls_context()
def test_configure_without_validate_certs(): context = tls.configure_tls_context(validate_certs=False) assert isinstance(context, ssl.SSLContext) assert context.verify_mode == ssl.CERT_NONE assert context.check_hostname is False
def test_configure_with_validate_certs(): context = tls.configure_tls_context(validate_certs=True) assert isinstance(context, ssl.SSLContext) assert context.verify_mode == ssl.CERT_REQUIRED assert context.check_hostname is True
def test_configure_with_no_args_works(): context = tls.configure_tls_context() assert isinstance(context, ssl.SSLContext)