Skip to content

Latest commit

 

History

History
130 lines (78 loc) · 8.07 KB

tasks.md

File metadata and controls

130 lines (78 loc) · 8.07 KB

Module 1 - Authentication

1.1 - Models: Password Column

Project Overview

In this module we'll alter the SQLAlchemy User model to include a password column. Using Flask-Migrate, we will add the new column to the database and populate the required fields. We will create a login HTML form and validate the form data in a new login route. The currently logged in user will be stored in a Flask session. The session will be cleared when the user logs out.

First Task

Open the file called models.py in the cms/admin folder. Find the User model, add a column of type string with a size of 100. Make sure nullable is False. Name this column password.

1.2 - Models: Check Password

Eventually we are going to need to verify the username and password of a user. There are a few functions that are part of werkzeug.security that can help us out. Import check_password_hash from werkzeug.security below the other imports.

The best place for a password check is in the User model itself. Add a function called check_password to the User model below the password column. Since check_password is part of a class pass two parameters, self and value.

In the body of check_password return a call to the check_password_hash function. Pass in the new class variable password (Hint: self.), and the value.

1.3 - Database Migration

There is currently no database for the application. Let's create one and migrate the new scheme that includes our new password column. Open a terminal, command propmt, or powershell and cd to the root folder of the project.

The Flask-Migrate extension should be installed. This exenstion provides several flask db commands.

  • First, to initialize and configure our schema run the flask db init command.
  • Second, to create a migration run the flask db migrate command.
  • Third, to create the database and run the migration use flask db upgrade.
  • Finally, run the custom command flask add-content to add content to the database.

1.4 - Template: Login Form

Open the login.html file found in the templates folder of the admin blueprint. This template contains a <form> element with several empty <div>s. Each one having a class of control. Let's add a form control to each one.

Find the label with the text, Username. In the control <div> below, add a text field that has a name of username and a class of input. Find the label with the text, Password. In the control <div> below, add a text field that has a name of password and a class of input.

In the last empty <div> towards the bottom, add a submit button that has a value of Submit. Give it two classes, button and is-link.

1.5 - Auth: Imports

The auth.py file in cms/admin will contain all authentication related code. Open it and at the top add the following imports:

  • import wraps from functools
  • import session and g from flask

These will be necessary for when we create our custom authentication decorator. Note: Unless otherwise noted, the rest of the tasks in this module happen in the file auth.py.

1.6 - Auth: Protected Decorator

To require users to login when accessing any of the admin dashboard routes i.e. /admin lets create a custom route decorator. Below the imports create a function called protected. The first parameter to the function should be called route_function.

In the body of this new function create another function called wrapped_route_function. To allow this function to accept an arbitry number of arguments use **kwargs as the first parameter.

For now the only statement in the body of the wrapped_route_function should return a call to the route_function. Pass **kwargs as the first argument.

1.7 - Auth: Redirect User

For the decorator to correctly wrap the route function, add the @wraps decorator to the wrapped_route_function function. Make sure to pass route_function to the decorator.

Next, in the body of wrapped_route_function above the return statement, add an if statement that tests whether g.user is None. In the if return a redirect that points to admin.login. Hint: you will need a url_for function.

At the bottom of the protected function, make sure you are not in the wrapped_route_function function, return wrapped_route_function.

1.8 - Auth: Load User

Below the new decorator create a new function called load_user. As the first line get the user_id from the session and store it in a variable called user_id. Decorate the function with the before_app_request decorator. Hint: auth.py is part of the admin_bp blueprint.

As the last line of load_user use a ternary if to assign g.user the result of User.query.get(user_id) if user_id is not None else assign it None.

1.9 - Auth: Login Route

Let's create a new route function called login. Add a route decorator with a URL pattern of /login. Make sure this new route allows GET and POST requests. Note: this route is part of our admin_bp blueprint.

In the body render the 'admin/login.html' template, make sure to return the results.

1.10 - Auth: Post Request

Above the redirect statements, add an if that checks if the request method is POST. If so, assign create two variable, username and password and assign each the appropriate form data. We are going to validate some of our form data on the server side, so, set a new variable called error to None.

1.11 - Auth: Get User & Check Password

Let's check if a user exists with the username that is provided. query the User model and filter_by() the username. Make sure to take just the first() row and assign it to a user variable.

Check the password of the user by calling check_password on the user object. Assign the result to a variable called check.

1.12 - Auth: Validate Form Data

We want to make sure the user exists, so in an if statement check if user is None. Also verify the password is correct. Note: Use an elif, check should not be None. Both the if and elif should set error to an appropriate message.

1.13 - Auth: Store User in Session

If there is no error clear() the the session. Also, store the value of user.id in the session key user_id. Then finally redirect to admin.content with type set to 'page'.

Outside the error if statement flash() the error.

1.14 - Auth: Logout Route

Let's create a new route function called logout. Add a route decorator with a URL pattern of /logout. Note: this route is part of our admin_bp blueprint.

In the body clear() the session, and return a redirect to admin.login.

Switch to layout.html and find the <div> that has a class of nav-item. To this <div> add an anchor element that says Logout and has two classes, button and is-light. In the href attribute use the url_for() function to point to the admin.logout route.

1.15 - Admin: Protect Routes

A few last things, open the cms/admin/__init__.py file. Below the admin_bp variable, import auth from cms.admin. This placement is important.

Finally, protect all the routes in the admin blueprint with the @auth.protected custom decorator. This should include these routes: content, create, edit, users and settings.