pythonpostgresqlsqlalchemysftpparamiko

"TypeError: string argument without an encoding" when retrieving an ORM object


I'm trying to set up an SFTP server in python, using paramiko library. I'm using a PostgreSQL to store users credentials, by SQLAlchemy library.

When I try to login using FileZilla or WinSCP to the server, it gives me an error in this function:

def authenticate_user(username: str, password:str = None, public_key:str = None) -> bool:
engine = provide_engine()
if not engine:
    return False

Session = sessionmaker(bind=engine)
session = Session()

try:
    stmt = select(User).where(User.username == username)
    user = session.execute(stmt).scalars().first()

    if not user:
        return False

    if password:
        print("Before checking.")
        if user.check_password(password):
            print("After checking.")
            return True
    elif public_key and user.ssh_keys and len(user.ssh_keys) > 0:
        for key in user.ssh_keys:
            if key.public_key == public_key:
                return True
            
    return False
finally:
    session.close()

The error is:

ERROR:paramiko.transport:Unknown exception: string argument without an encoding
ERROR:paramiko.transport:Traceback (most recent call last):
ERROR:paramiko.transport:  File "C:\Users\*******\AppData\Local\Programs\Python\Python312\Lib\site-packages\paramiko\transport.py", line 2262, in run
ERROR:paramiko.transport:    handler(m)
ERROR:paramiko.transport:  File "C:\Users\*******\AppData\Local\Programs\Python\Python312\Lib\site-packages\paramiko\auth_handler.py", line 609, in _parse_userauth_request
ERROR:paramiko.transport:    result = self.transport.server_object.check_auth_password(
ERROR:paramiko.transport:             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ERROR:paramiko.transport:  File "d:\Files\*******\Python\cr-beta\database_sftp\SFTPServer.py", line 20, in check_auth_password
ERROR:paramiko.transport:    if authenticate_user(username, password=password):
ERROR:paramiko.transport:       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ERROR:paramiko.transport:  File "d:\Files\*******\Python\cr-beta\database_sftp\SFTPServer.py", line 98, in authenticate_user
ERROR:paramiko.transport:    user = session.execute(stmt).scalars().first()
ERROR:paramiko.transport:           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ERROR:paramiko.transport:  File "C:\Users\*******\AppData\Local\Programs\Python\Python312\Lib\site-packages\sqlalchemy\engine\result.py", line 1786, in first  
ERROR:paramiko.transport:    return self._only_one_row(
ERROR:paramiko.transport:           ^^^^^^^^^^^^^^^^^^^
ERROR:paramiko.transport:  File "C:\Users\*******\AppData\Local\Programs\Python\Python312\Lib\site-packages\sqlalchemy\engine\result.py", line 749, in _only_one_row
ERROR:paramiko.transport:    row: Optional[_InterimRowType[Any]] = onerow(hard_close=True)
ERROR:paramiko.transport:                                          ^^^^^^^^^^^^^^^^^^^^^^^
ERROR:paramiko.transport:  File "C:\Users\*******\AppData\Local\Programs\Python\Python312\Lib\site-packages\sqlalchemy\engine\result.py", line 1673, in _fetchone_impl
ERROR:paramiko.transport:    return self._real_result._fetchone_impl(hard_close=hard_close)
ERROR:paramiko.transport:           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ERROR:paramiko.transport:  File "C:\Users\*******\AppData\Local\Programs\Python\Python312\Lib\site-packages\sqlalchemy\engine\result.py", line 2259, in _fetchone_impl
ERROR:paramiko.transport:    row = next(self.iterator, _NO_ROW)
ERROR:paramiko.transport:          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ERROR:paramiko.transport:  File "C:\Users\*******\AppData\Local\Programs\Python\Python312\Lib\site-packages\sqlalchemy\orm\loading.py", line 219, in chunks    
ERROR:paramiko.transport:    fetch = cursor._raw_all_rows()
ERROR:paramiko.transport:            ^^^^^^^^^^^^^^^^^^^^^^
ERROR:paramiko.transport:  File "C:\Users\*******\AppData\Local\Programs\Python\Python312\Lib\site-packages\sqlalchemy\engine\result.py", line 541, in _raw_all_rows
ERROR:paramiko.transport:    return [make_row(row) for row in rows]
ERROR:paramiko.transport:            ^^^^^^^^^^^^^
ERROR:paramiko.transport:  File "lib\\sqlalchemy\\cyextension\\resultproxy.pyx", line 22, in sqlalchemy.cyextension.resultproxy.BaseRow.__init__
ERROR:paramiko.transport:  File "lib\\sqlalchemy\\cyextension\\resultproxy.pyx", line 79, in sqlalchemy.cyextension.resultproxy._apply_processors
ERROR:paramiko.transport:  File "C:\Users\*******\AppData\Local\Programs\Python\Python312\Lib\site-packages\sqlalchemy\sql\sqltypes.py", line 913, in process  
ERROR:paramiko.transport:    value = bytes(value)
ERROR:paramiko.transport:            ^^^^^^^^^^^^
ERROR:paramiko.transport:TypeError: string argument without an encoding

I don't understand wher is the problem, also because, the username is stored as string in the Database, and the password i think that is handled well. Under the declaraton of the User table:

class User(Base):
__tablename__ = USERS_TABLE
id = Column(Integer, primary_key=True)
username = Column(String, unique=True, nullable=False)
password_hash = Column(LargeBinary, nullable=False)
created_at = Column(DateTime, default=datetime.now(timezone.utc))

ssh_keys = relationship("SSHKey", back_populates="user")

def set_hashed_password(self, hashed_password: bytes):
    self.password_hash = hashed_password

def set_password(self, password: str):
    self.password_hash = bcrypt.hashpw(password.encode(), bcrypt.gensalt())

def check_password(self, password: str) -> bool:
    return bcrypt.checkpw(password.encode(), self.password_hash)

I want to specify that i'm trying to login with a username and a password, and not with a ssh key.

UPDATE: I tried to check the real column type using:

from sqlalchemy import inspect
insp = inspect(engine)
print(list(x["type"] for x in insp.get_columns(USERS_TABLE) if x["name"] == "password_hash"))

and it print [VARCHAR()] in console.

I also check in pgAdmin4 and the type of the column is character varying.

pgAdmin4 screenshot

Why if in User class I set column as LargeBinary it is a variable character? Is this the problem?


Solution

  • PostgreSQL tables has been created with a wrong type. The password_hashed column type was character varying, becouse the type was changed later.