Now, we will create the model that we will use to represent and persist the user. Open the api/models.py
file and add the following lines after the declaration of the AddUpdateDelete
class. Make sure that you add the import statements. The code file for the sample is included in the restful_python_chapter_07_02
folder:
from passlib.apps import custom_app_context as password_context import re class User(db.Model, AddUpdateDelete): id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(50), unique=True, nullable=False) # I save the hashed password hashed_password = db.Column(db.String(120), nullable=False) creation_date = db.Column(db.TIMESTAMP, server_default=db.func.current_timestamp(), nullable=False) def verify_password(self, password): return password_context.verify(password, self.hashed_password) def check_password_strength_and_hash_if_ok(self, password): if len(password) < 8: return 'The password is too short', False if len(password) > 32: return 'The password is too long', False if re.search(r'[A-Z]', password) is None: return 'The password must include at least one uppercase letter', False if re.search(r'[a-z]', password) is None: return 'The password must include at least one lowercase letter', False if re.search(r'd', password) is None: return 'The password must include at least one number', False if re.search(r"[ !#$%&'()*+,-./[\]^_`{|}~"+r'"]', password) is None: return 'The password must include at least one symbol', False self.hashed_password = password_context.encrypt(password) return '', True def __init__(self, name): self.name = name
The code declares the User
model, specifically a subclasses of both the db.Model
and the AddUpdateDelete
classes. We specified the field types, maximum lengths and defaults for the following three attributes-id
, name
, hashed_password
and creation_date
. These attributes represent fields without any relationship, and therefore, they are instances of the db.Column
class. The model declares an id
attribute and specifies the True
value for the primary_key
argument to indicate it is the primary key. SQLAlchemy will use the data to generate the necessary table in the PostgreSQL database.
The User
class declares the following methods:
check_password_strength_and_hash_if_ok
: This method uses the re
module that provides regular expression matching operations to check whether the password
received as an argument fulfils many qualitative requirements. The code requires the password to be longer than eight characters, with a maximum of 32 characters. The password must include at least one uppercase letter, one lowercase letter, one number, and one symbol. The code checks the results of many calls to the re.search
method to determine whether the received password fulfils each requirement. In case any of the requirements isn't fulfilled, the code returns a tuple with an error message and False
. Otherwise, the code calls the encrypt
method for the passlib.apps.custom_app_context
instance imported as password_context
, with the received password
as an argument. The encrypt
method chooses a reasonably strong scheme based on the platform, with the default settings for rounds selection and the code saves the hashed password in the hash_password
attribute. Finally, the code returns a tuple with an empty string and True
, indicating that the password fulfilled the qualitative requirements and it was hashed.By default, the passlib
library will use the SHA-512 scheme for 64-bit platforms and SHA-256 for 32-bit platforms. In addition, the minimum number of rounds will be set to 535,000. We will use the default configuration values for this example. However, you must take into account that these values might require too much processing time for each request that has to validate the password. You should definitely select the most appropriate algorithm and number of rounds based on your security requirements.
verify_password
: This method calls the verify
method for the passlib.apps.custom_app_context
instance imported as password_context
, with the received password
and the stored hashed password for the user, self.hashed_password
, as the arguments. The verify
method hashes the received password and returns True
only if the hashed received password matches the stored hashed password. We never restore the saved password to its original state. We just compare hashed values.The model declares a constructor, that is, the __init__
method. This constructor receives the user name in the name
argument and saves it in an attribute with the same name.
3.15.31.22