mirror of https://github.com/pallets/flask.git
safer check for existing user in tutorial
Co-authored-by: David Lord <davidism@gmail.com>
This commit is contained in:
parent
50b7dcbab3
commit
5119657547
|
@ -91,18 +91,18 @@ write templates to generate the HTML form.
|
||||||
error = 'Username is required.'
|
error = 'Username is required.'
|
||||||
elif not password:
|
elif not password:
|
||||||
error = 'Password is required.'
|
error = 'Password is required.'
|
||||||
elif db.execute(
|
|
||||||
'SELECT id FROM user WHERE username = ?', (username,)
|
|
||||||
).fetchone() is not None:
|
|
||||||
error = f"User {username} is already registered."
|
|
||||||
|
|
||||||
if error is None:
|
if error is None:
|
||||||
|
try:
|
||||||
db.execute(
|
db.execute(
|
||||||
'INSERT INTO user (username, password) VALUES (?, ?)',
|
"INSERT INTO user (username, password) VALUES (?, ?)",
|
||||||
(username, generate_password_hash(password))
|
(username, generate_password_hash(password)),
|
||||||
)
|
)
|
||||||
db.commit()
|
db.commit()
|
||||||
return redirect(url_for('auth.login'))
|
except db.IntegrityError:
|
||||||
|
error = f"User {username} is already registered."
|
||||||
|
else:
|
||||||
|
return redirect(url_for("auth.login"))
|
||||||
|
|
||||||
flash(error)
|
flash(error)
|
||||||
|
|
||||||
|
@ -125,26 +125,25 @@ Here's what the ``register`` view function is doing:
|
||||||
|
|
||||||
#. Validate that ``username`` and ``password`` are not empty.
|
#. Validate that ``username`` and ``password`` are not empty.
|
||||||
|
|
||||||
#. Validate that ``username`` is not already registered by querying the
|
|
||||||
database and checking if a result is returned.
|
|
||||||
:meth:`db.execute <sqlite3.Connection.execute>` takes a SQL query
|
|
||||||
with ``?`` placeholders for any user input, and a tuple of values
|
|
||||||
to replace the placeholders with. The database library will take
|
|
||||||
care of escaping the values so you are not vulnerable to a
|
|
||||||
*SQL injection attack*.
|
|
||||||
|
|
||||||
:meth:`~sqlite3.Cursor.fetchone` returns one row from the query.
|
|
||||||
If the query returned no results, it returns ``None``. Later,
|
|
||||||
:meth:`~sqlite3.Cursor.fetchall` is used, which returns a list of
|
|
||||||
all results.
|
|
||||||
|
|
||||||
#. If validation succeeds, insert the new user data into the database.
|
#. If validation succeeds, insert the new user data into the database.
|
||||||
For security, passwords should never be stored in the database
|
|
||||||
|
- :meth:`db.execute <sqlite3.Connection.execute>` takes a SQL
|
||||||
|
query with ``?`` placeholders for any user input, and a tuple of
|
||||||
|
values to replace the placeholders with. The database library
|
||||||
|
will take care of escaping the values so you are not vulnerable
|
||||||
|
to a *SQL injection attack*.
|
||||||
|
|
||||||
|
- For security, passwords should never be stored in the database
|
||||||
directly. Instead,
|
directly. Instead,
|
||||||
:func:`~werkzeug.security.generate_password_hash` is used to
|
:func:`~werkzeug.security.generate_password_hash` is used to
|
||||||
securely hash the password, and that hash is stored. Since this
|
securely hash the password, and that hash is stored. Since this
|
||||||
query modifies data, :meth:`db.commit() <sqlite3.Connection.commit>`
|
query modifies data,
|
||||||
needs to be called afterwards to save the changes.
|
:meth:`db.commit() <sqlite3.Connection.commit>` needs to be
|
||||||
|
called afterwards to save the changes.
|
||||||
|
|
||||||
|
- An :exc:`sqlite3.IntegrityError` will occur if the username
|
||||||
|
already exists, which should be shown to the user as another
|
||||||
|
validation error.
|
||||||
|
|
||||||
#. After storing the user, they are redirected to the login page.
|
#. After storing the user, they are redirected to the login page.
|
||||||
:func:`url_for` generates the URL for the login view based on its
|
:func:`url_for` generates the URL for the login view based on its
|
||||||
|
@ -200,6 +199,11 @@ There are a few differences from the ``register`` view:
|
||||||
|
|
||||||
#. The user is queried first and stored in a variable for later use.
|
#. The user is queried first and stored in a variable for later use.
|
||||||
|
|
||||||
|
:meth:`~sqlite3.Cursor.fetchone` returns one row from the query.
|
||||||
|
If the query returned no results, it returns ``None``. Later,
|
||||||
|
:meth:`~sqlite3.Cursor.fetchall` will be used, which returns a list
|
||||||
|
of all results.
|
||||||
|
|
||||||
#. :func:`~werkzeug.security.check_password_hash` hashes the submitted
|
#. :func:`~werkzeug.security.check_password_hash` hashes the submitted
|
||||||
password in the same way as the stored hash and securely compares
|
password in the same way as the stored hash and securely compares
|
||||||
them. If they match, the password is valid.
|
them. If they match, the password is valid.
|
||||||
|
|
|
@ -60,20 +60,20 @@ def register():
|
||||||
error = "Username is required."
|
error = "Username is required."
|
||||||
elif not password:
|
elif not password:
|
||||||
error = "Password is required."
|
error = "Password is required."
|
||||||
elif (
|
|
||||||
db.execute("SELECT id FROM user WHERE username = ?", (username,)).fetchone()
|
|
||||||
is not None
|
|
||||||
):
|
|
||||||
error = f"User {username} is already registered."
|
|
||||||
|
|
||||||
if error is None:
|
if error is None:
|
||||||
# the name is available, store it in the database and go to
|
try:
|
||||||
# the login page
|
|
||||||
db.execute(
|
db.execute(
|
||||||
"INSERT INTO user (username, password) VALUES (?, ?)",
|
"INSERT INTO user (username, password) VALUES (?, ?)",
|
||||||
(username, generate_password_hash(password)),
|
(username, generate_password_hash(password)),
|
||||||
)
|
)
|
||||||
db.commit()
|
db.commit()
|
||||||
|
except db.IntegrityError:
|
||||||
|
# The username was already taken, which caused the
|
||||||
|
# commit to fail. Show a validation error.
|
||||||
|
error = f"User {username} is already registered."
|
||||||
|
else:
|
||||||
|
# Success, go to the login page.
|
||||||
return redirect(url_for("auth.login"))
|
return redirect(url_for("auth.login"))
|
||||||
|
|
||||||
flash(error)
|
flash(error)
|
||||||
|
|
Loading…
Reference in New Issue