Reader small image

You're reading from  Building RESTful Python Web Services

Product typeBook
Published inOct 2016
Reading LevelIntermediate
PublisherPackt
ISBN-139781786462251
Edition1st Edition
Languages
Concepts
Right arrow
Author (1)
Gaston C. Hillar
Gaston C. Hillar
author image
Gaston C. Hillar

Gaston C. Hillar is Italian and has been working with computers since he was 8 years old. Gaston has a Bachelor's degree in computer science (graduated with honors) and an MBA. Currently, Gaston is an independent IT consultant and a freelance author who is always looking for new adventures anywhere in the world. He was a senior contributing editor at Dr. Dobb's, and has written more than a hundred articles on software development topics. He has received the prestigious Intel Black Belt Software Developer award eight times. He has written many articles about Java for Oracle Java Magazine. Gaston was also a former Microsoft MVP in technical computing. He lives with his wife, Vanesa, and his two sons, Kevin and Brandon.
Read more about Gaston C. Hillar

Right arrow

Chapter 7.  Improving and Adding Authentication to an API with Flask

In this chapter, we will improve the RESTful API that we started in the previous chapter and we will add authentication related security to it. We will:

  • Improve unique constraints in the models

  • Update fields for a resource with the PATCH method

  • Code a generic pagination class

  • Add pagination features to the API

  • Understand the steps to add authentication and permissions

  • Add a user model

  • Create a schema to validate, serialize and deserialize users

  • Add authentication to resources

  • Create resource classes to handle users

  • Run migrations to generate the user table

  • Compose requests with the necessary authentication

Improving unique constraints in the models


When we created the Category model, we specified the True value for the unique argument when we created the db.Column instance named name. As a result, the migrations generated the necessary unique constraint to make sure that the name field has unique values in the category table. This way, the database won't allow us to insert duplicate values for category.name. However, the error message generated when we try to do so is not clear.

Run the following command to create a category with a duplicate name. There is already an existing category with the name equal to 'Information':

http POST :5000/api/categories/ name='Information'

The following is the equivalent curl command:

curl -iX POST -H "Content-Type: application/json" -d '{"name":"Information"}'
    :5000/api/categories/

The previous command will compose and send a POST HTTP request with the specified JSON key-value pair. The unique constraint in the category.name field won't allow the database...

Updating fields for a resource with the PATCH method


As we explained in Chapter 6 , Working with Models, SQLAlchemy, and Hyperlinked APIs in Flask, our API is able to update a single field for an existing resource, and therefore, we provide an implementation for the PATCH method. For example, we can use the PATCH method to update an existing message and set the value for its printed_once and printed_times fields to true and 1. We don't want to use the PUT method because this method is meant to replace an entire message. The PATCH method is meant to apply a delta to an existing message, and therefore, it is the appropriate method to just change the value of those two fields.

Now, we will compose and send an HTTP request to update an existing message, specifically, to update the value of the printed_once and printed_times fields. Because we just want to update two fields, we will use the PATCH method instead of PUT. Make sure you replace 1 with the id or primary key of an existing message in...

Coding a generic pagination class


Our database has a few rows for each of the tables that persist the models we have defined. However, after we start working with our API in a real-life production environment, we will have hundreds of messages, and therefore, we will have to deal with large result sets. Thus, we will create a generic pagination class and we will use it to easily specify how we want large results sets to be split into individual pages of data.

First, we will compose and send HTTP requests to create 9 messages that belong to one of the categories we have created: Information. This way, we will have a total of 12 messages persisted in the database. We had 3 messages and we add 9 more.

http POST :5000/api/messages/ message='Initializing light controller' duration=25 category="Information"
http POST :5000/api/messages/ message='Initializing light sensor' duration=20 category="Information"
http POST :5000/api/messages/ message='Checking pressure sensor' duration=18 category="Information...

Adding pagination features


Open the api/views.py file and replace the code for the MessageListResource.get method with the highlighted lines in the next listing. In addition, make sure that you add the import statement. The code file for the sample is included in the restful_python_chapter_07_01 folder:

from helpers import PaginationHelper 
 
 
class MessageListResource(Resource): 
    def get(self): 
        pagination_helper = PaginationHelper( 
            request, 
            query=Message.query, 
            resource_for_url='api.messagelistresource', 
            key_name='results', 
            schema=message_schema) 
        result = pagination_helper.paginate_query() 
        return result

The new code for the get method creates an instance of the previously explained PaginationHelper class named pagination_helper with the request object as the first argument. The named arguments specify the query, resource_for_url, key_name, and schema that the PaginationHelper instance has to...

Understanding the steps to add authentication and permissions


Our current version of the API processes all the incoming requests without requiring any kind of authentication. We will use a Flask extension and other packages to use an HTTP authentication scheme to identify the user that originated the request or the token that signed the request. Then, we will use these credentials to apply the permissions that will determine whether the request must be permitted or not. Unluckily, neither Flask nor Flask-RESTful provides an authentication framework that we can easily plug and configure. Thus, we will have to write code to perform many tasks related to authentication and permissions.

We want to be able to create a new user without any authentication. However, all the other API calls are only going to be available for authenticated users.

First, we will install a Flask extension to make it easier for us to work with HTTP authentication, Flask-HTTPAuth, and a package to allow us to hash a password...

Adding a user model


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: 
    ...

Creating a schemas to validate, serialize, and deserialize users


Now, we will create the Flask-Marshmallow schema that we will use to validate, serialize and deserialize the previously declared User model. Open the api/models.py file and add the following code after the existing lines. The code file for the sample is included in the restful_python_chapter_07_02 folder:

class UserSchema(ma.Schema): 
    id = fields.Integer(dump_only=True) 
    name = fields.String(required=True, validate=validate.Length(3)) 
    url = ma.URLFor('api.userresource', id='<id>', _external=True) 

The code declares the UserSchema schema, specifically a subclass of the ma.Schema class. Remember that the previous code we wrote for the api/models.py file created a flask_marshmallow.Mashmallow instance named ma.

We declare the attributes that represent fields as instances of the appropriate class declared in the marshmallow.fields module. The UserSchema class declares the name attribute as an instance of fields...

Adding authentication to resources


We will configure the Flask-HTTPAuth extension to work with our User model to verify passwords and set the authenticated user associated with a request. We will declare a custom function that this extension will use as a callback to verify a password. We will create a new base class for our resources that will require authentication. Open the api/views.py file and add the following code after the last line that uses the import statement and before the lines that declares the Blueprint instance . The code file for the sample is included in the restful_python_chapter_07_02 folder:

from flask_httpauth import HTTPBasicAuth 
from flask import g 
from models import User, UserSchema 
 
 
auth = HTTPBasicAuth() 
 
 
@auth.verify_password 
def verify_user_password(name, password): 
    user = User.query.filter_by(name=name).first() 
    if not user or not user.verify_password(password): 
        return False 
    g.user = user 
    return True 
 
 
class AuthRequiredResource...

Creating resource classes to handle users


We just want to be able to create users and use them to authenticate requests. Thus, we will just focus on creating resource classes with just a few methods. We won't create a complete user management system.

We will create the resource classes that represent the user and the collection of users. First, we will create a UserResource class that we will use to represent a user resource. Open the api/views.py file and add the following lines after the line that creates the Api instance. The code file for the sample is included in the restful_python_chapter_07_02 folder:

class UserResource(AuthRequiredResource): 
    def get(self, id): 
        user = User.query.get_or_404(id) 
        result = user_schema.dump(user).data 
        return result 

The UserResource class is a subclass of the previously coded AuthRequiredResource and declares a get methods that will be called when the HTTP method with the same name arrives as a request on the represented resource...

Running migrations to generate the user table


Now, we will run many scripts to run migrations and generate the necessary table in the PostgreSQL database. Make sure you run the scripts in the terminal or the Command Prompt window in which you have activated the virtual environment and that you are located in the api folder.

Run the first script that populates the migration script with the detected changes in the models. In this case, it is the second time we populate the migration script, and therefore, the migration script will generate the new table that will persist our new User model: model:

python migrate.py db migrate

The following lines show the sample output generated after running the previous script. Your output will be different according to the base folder in which you have created the virtual environment.

INFO [alembic.runtime.migration] Context impl PostgresqlImpl.
INFO [alembic.runtime.migration] Will assume transactional DDL.
INFO [alembic.autogenerate.compare] Detected added...

Composing requests with the necessary authentication


Now, we will compose and send an HTTP request to retrieve the first page of the messages without authentication credentials:

http POST ':5000/api/messages/?page=1'

The following is the equivalent curl command:

curl -iX GET ':5000/api/messages/?page=1'

We will receive a 401 Unauthorized status code in the response header. The following lines show a sample response:

HTTP/1.0 401 UNAUTHORIZED
Content-Length: 19
Content-Type: text/html; charset=utf-8
Date: Mon, 15 Aug 2016 01:16:36 GMT
Server: Werkzeug/0.11.10 Python/3.5.1
WWW-Authenticate: Basic realm="Authentication Required"

If we want to retrieve messages, that is, to make a GET request to /api/messages/, we need to provide authentication credentials using HTTP authentication. However, before we can do this, it is necessary to create a new user. We will use the new user to test our new resource classes related to users and our changes in the permissions policies.

http POST :5000/api/users...

Test your knowledge


  1. The flask.g object is:

    1. A proxy that provides access to the current request.

    2. An instance of the flask_httpauth.HTTPBasicAuth class.

    3. A proxy that allows us to store on this whatever we want to share for one request only.

  2. The passlib package provides:

    1. A password hashing framework that supports more than 30 schemes.

    2. An authentication framework that automatically adds models for users and permissios to a Flask application.

    3. A lightweight web framework that replaces Flask.

  3. The auth.verify_password decorator applied to a function:

    1. Makes this function become the callback that Flask-HTTPAuth will use to hash the password for a specific user.

    2. Makes this function become the callback that SQLAlchmey will use to verify the password for a specific user.

    3. Makes this function become the callback that Flask-HTTPAuth will use to verify the password for a specific user.

  4. When you assign a list that includes auth.login_required to the method_decorators property of any subclass of flask_restful.Resource...

Summary


In this chapter, we improved the RESTful API in many ways. We added user friendly error messages when resources aren't unique. We tested how to update single or multiple fields with the PATCH method and we created our own generic pagination class.

Then, we started working with authentication and permissions. We added a user model and we updated the database. We made many changes in the different pieces of code to achieve a specific security goal and we took advantage of Flask-HTTPAuth and passlib to use HTTP authentication in our API.

Now that we have built an improved a complex API that uses pagination and authentication, we will use additional abstractions included in the framework and we will code, execute, and improve unit test, which is what we are going to discuss in the next chapter.

lock icon
The rest of the chapter is locked
You have been reading a chapter from
Building RESTful Python Web Services
Published in: Oct 2016Publisher: PacktISBN-13: 9781786462251
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
undefined
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $15.99/month. Cancel anytime

Author (1)

author image
Gaston C. Hillar

Gaston C. Hillar is Italian and has been working with computers since he was 8 years old. Gaston has a Bachelor's degree in computer science (graduated with honors) and an MBA. Currently, Gaston is an independent IT consultant and a freelance author who is always looking for new adventures anywhere in the world. He was a senior contributing editor at Dr. Dobb's, and has written more than a hundred articles on software development topics. He has received the prestigious Intel Black Belt Software Developer award eight times. He has written many articles about Java for Oracle Java Magazine. Gaston was also a former Microsoft MVP in technical computing. He lives with his wife, Vanesa, and his two sons, Kevin and Brandon.
Read more about Gaston C. Hillar