def test_secure_connect(self): target_server = object() insecure = SIPClient(target_server, 999, use_ssl=False) no_cert = SIPClient(target_server, 999, use_ssl=True) with_cert = SIPClient( target_server, 999, ssl_cert="cert", ssl_key="key" ) # Mock the socket.socket function. old_socket = socket.socket socket.socket = MockSocket # Mock the ssl.wrap_socket function old_wrap_socket = ssl.wrap_socket wrap_socket = MockWrapSocket() ssl.wrap_socket = wrap_socket try: # When an insecure connection is created, wrap_socket is # not called. insecure.connect() eq_(None, wrap_socket.called_with) # When a secure connection is created with no SSL # certificate, wrap_socket() is called on the connection # (in this case, a MockSocket), but no other arguments are # passed in to wrap_socket(). no_cert.connect() connection, kwargs = wrap_socket.called_with assert isinstance(connection, MockSocket) eq_(dict(keyfile=None, certfile=None), kwargs) # When a secure connection is created with an SSL # certificate, the certificate and key are written to # temporary files, and the paths to those files are passed # in along with the collection to wrap_socket(). wrap_socket.called_with = None with_cert.connect() connection, kwargs = wrap_socket.called_with assert isinstance(connection, MockSocket) eq_(set(['keyfile', 'certfile']), set(kwargs.keys())) for tmpfile in kwargs.values(): assert tmpfile.startswith("/tmp") # By the time the SSL socket has been wrapped, the # temporary file has already been removed. Because of # that we can't verify from within a unit test that the # correct contents were written to the file. assert not os.path.exists(tmpfile) finally: # Un-mock the old functions. socket.socket = old_socket ssl.wrap_socket = old_wrap_socket
def test_connect(self): target_server = object() sip = SIPClient(target_server, 999) old_socket = socket.socket # Mock the socket.socket function. socket.socket = MockSocket # Call connect() and make sure timeout is set properly. try: sip.connect() assert 12 == sip.connection.timeout finally: # Un-mock the socket.socket function socket.socket = old_socket
def __init__(self, library, integration, analytics=None, client=None, connect=True): """An object capable of communicating with a SIP server. :param server: Hostname of the SIP server. :param port: The port number to connect to on the SIP server. :param login_user_id: SIP field CN; the user ID to use when initiating a SIP session, if necessary. This is _not_ a patron identifier (SIP field AA); it identifies the SC creating the SIP session. SIP2 defines SC as "...any library automation device dealing with patrons or library materials." :param login_password: Sip field CO; the password to use when initiating a SIP session, if necessary. :param location_code: SIP field CP; the location code to use when initiating a SIP session. A location code supposedly refers to the physical location of a self-checkout machine within a library system. Some libraries require a special location code to be provided when authenticating patrons; others may require the circulation manager to be treated as its own special 'location'. :param field_separator: The field delimiter (see "Variable-length fields" in the SIP2 spec). If no value is specified, the default (the pipe character) will be used. :param client: A drop-in replacement for the SIPClient object. Only intended for use during testing. :param connect: If this is false, the generated SIPClient will not attempt to connect to the server. Only intended for use during testing. """ super(SIP2AuthenticationProvider, self).__init__( library, integration, analytics ) try: server = None if client: if callable(client): client = client() else: server = integration.url port = integration.setting(self.PORT).int_value login_user_id = integration.username login_password = integration.password location_code = integration.setting(self.LOCATION_CODE).value field_separator = integration.setting( self.FIELD_SEPARATOR).value or '|' client = SIPClient( target_server=server, target_port=port, login_user_id=login_user_id, login_password=login_password, location_code=location_code, separator=field_separator, connect=connect ) except IOError, e: raise RemoteIntegrationException( server or 'unknown server', e.message )
def test_connect(self): target_server = object() sip = SIPClient(target_server, 999) old_socket = socket.socket # Mock the socket.socket function. socket.socket = MockSocket # Call connect() and make sure timeout is set properly. try: sip.connect() eq_(12, sip.connection.timeout) finally: # Un-mock the socket.socket function socket.socket = old_socket
def _run_self_tests(self, _db): def makeConnection(sip): sip.connect() return sip.connection if self.client: sip = self.client else: sip = SIPClient(target_server=self.server, target_port=self.port, login_user_id=self.login_user_id, login_password=self.login_password, location_code=self.location_code, institution_id=self.institution_id, separator=self.field_separator, use_ssl=self.use_ssl, ssl_cert=self.ssl_cert, ssl_key=self.ssl_key, dialect=self.dialect) connection = self.run_test(("Test Connection"), makeConnection, sip) yield connection if not connection.success: return login = self.run_test( ("Test Login with username '%s' and password '%s'" % (self.login_user_id, self.login_password)), sip.login) yield login # Log in was successful so test patron's test credentials if login.success: results = [ r for r in super(SIP2AuthenticationProvider, self)._run_self_tests(_db) ] for result in results: yield result if results[0].success: def raw_patron_information(): info = sip.patron_information(self.test_username, self.test_password) return json.dumps(info, indent=1) yield self.run_test("Patron information request", sip.patron_information_request, self.test_username, patron_password=self.test_password) yield self.run_test(("Raw test patron information"), raw_patron_information)
def test_read_message(self): target_server = object() sip = SIPClient(target_server, 999) old_socket = socket.socket # Mock the socket.socket function. socket.socket = MockSocket try: sip.connect() conn = sip.connection # Queue bytestrings and read them. for data in ( # Simple message. b"abcd\n", # Message that contains non-ASCII characters. "LE CARRÉ, JOHN\r".encode("cp850"), # Message that spans multiple blocks. (b"a" * 4097) + b"\n", ): conn.queue_data(data) assert data == sip.read_message() # IOError on a message that's too large. conn.queue_data("too big\n") with pytest.raises(IOError, match="SIP2 response too large."): sip.read_message(max_size=2) # IOError if transmission stops without ending on a newline. conn.queue_data("no newline") with pytest.raises(IOError, match="No data read from socket."): sip.read_message() finally: # Un-mock the socket.socket function socket.socket = old_socket
def test_secure_connect(self): target_server = object() insecure = SIPClient(target_server, 999, use_ssl=False) no_cert = SIPClient(target_server, 999, use_ssl=True) with_cert = SIPClient(target_server, 999, ssl_cert="cert", ssl_key="key") # Mock the socket.socket function. old_socket = socket.socket socket.socket = MockSocket # Mock the ssl.wrap_socket function old_wrap_socket = ssl.wrap_socket wrap_socket = MockWrapSocket() ssl.wrap_socket = wrap_socket try: # When an insecure connection is created, wrap_socket is # not called. insecure.connect() assert None == wrap_socket.called_with # When a secure connection is created with no SSL # certificate, wrap_socket() is called on the connection # (in this case, a MockSocket), but no other arguments are # passed in to wrap_socket(). no_cert.connect() connection, kwargs = wrap_socket.called_with assert isinstance(connection, MockSocket) assert dict(keyfile=None, certfile=None) == kwargs # When a secure connection is created with an SSL # certificate, the certificate and key are written to # temporary files, and the paths to those files are passed # in along with the collection to wrap_socket(). wrap_socket.called_with = None with_cert.connect() connection, kwargs = wrap_socket.called_with assert isinstance(connection, MockSocket) assert set(["keyfile", "certfile"]) == set(kwargs.keys()) for tmpfile in list(kwargs.values()): tmpfile = os.path.abspath(tmpfile) assert os.path.basename(tmpfile).startswith("tmp") # By the time the SSL socket has been wrapped, the # temporary file has already been removed. Because of # that we can't verify from within a unit test that the # correct contents were written to the file. assert not os.path.exists(tmpfile) finally: # Un-mock the old functions. socket.socket = old_socket ssl.wrap_socket = old_wrap_socket
def patron_information(self, username, password): try: if self.client: sip = self.client else: sip = SIPClient(target_server=self.server, target_port=self.port, login_user_id=self.login_user_id, login_password=self.login_password, location_code=self.location_code, institution_id=self.institution_id, separator=self.field_separator, use_ssl=self.use_ssl, ssl_cert=self.ssl_cert, ssl_key=self.ssl_key, dialect=self.dialect) sip.connect() sip.login() info = sip.patron_information(username, password) sip.end_session(username, password) sip.disconnect() return info except IOError, e: raise RemoteIntegrationException(self.server or 'unknown server', e.message)