Security

For user authentication security, we will use Flask's extension httpauth, written by Miguel Grinberg, as well as the password functions in Werkzeug. The httpauth extension should have been installed as part of the requirements.txt installation in the beginning of the chapter. The file is named chapter9_9.py; we will start with a few more module imports:

...
from
werkzeug.security import generate_password_hash, check_password_hash
from flask.ext.httpauth import HTTPBasicAuth
...

We will create an HTTPBasicAuth object as well as the user database object. Note that during the user creation process, we will pass the password value; however, we are only storing password_hash instead of the password itself:

auth = HTTPBasicAuth()

class User(db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(64), index=True)
password_hash = db.Column(db.String(128))

def set_password(self, password):
self.password_hash = generate_password_hash(password)

def verify_password(self, password):
return check_password_hash(self.password_hash, password)

The auth object has a verify_password decorator that we can use, along with Flash's g global context object that was created when the request started for password verification. Because g is global, if we save the user to the g variable, it will live through the entire transaction:

@auth.verify_password
def verify_password(username, password):
g.user = User.query.filter_by(username=username).first()
if g.user is None:
return False
return g.user.verify_password(password)

There is a handy before_request handler that can be used before any API endpoint is called. We will combine the auth.login_required decorator with the before_request handler that will be applied to all the API routes:

@app.before_request
@auth.login_required
def before_request():
pass

Lastly, we will use the unauthorized error handler to return a response object for the 401 unauthorized error:

@auth.error_handler
def unauthorized():
response = jsonify({'status': 401, 'error': 'unauthorized',
'message': 'please authenticate'})

response.status_code = 401
return response

Before we can test user authentication, we will need to create users in our database:

>>> from chapter9_9 import db, User
>>> db.create_all()
>>> u = User(username='eric')
>>> u.set_password('secret')
>>> db.session.add(u)
>>> db.session.commit()
>>> exit()

Once you start your Flask development server, try to make a request as before; however, this time it will be rejected and presented with a 401 unauthorized error:

$ http GET http://172.16.1.173:5000/devices/
HTTP/1.0 401 UNAUTHORIZED
Content-Length: 81
Content-Type: application/json
Date: <skip>
Server: Werkzeug/0.9.6 Python/3.5.2
WWW-Authenticate: Basic realm="Authentication Required"

{
"error": "unauthorized",
"message": "please authenticate",
"status": 401
}

We will now need to provide the authentication header for our requests:

$ http --auth eric:secret GET http://172.16.1.173:5000/devices/
HTTP/1.0 200 OK
Content-Length: 188
Content-Type: application/json
Date: <skip>
Server: Werkzeug/0.9.6 Python/3.5.2

{
"device": [
"http://172.16.1.173:5000/devices/1",
"http://172.16.1.173:5000/devices/2",
"http://172.16.1.173:5000/devices/3",
"http://172.16.1.173:5000/devices/4"
]
}

We now have a decent RESTful API setup for our network. The user will be able to interact with the APIs now instead of the network devices. They can query for static contents of the network as well as perform tasks for individual devices or a group of devices. We also added basic security measures to ensure only the users we created are able to retrieve the information from our API.

We have now abstracted the underlying vendor API away from our network and replaced them with our own RESTful API. We are free to use what is required, such as Pexpect, while still providing a uniform frontend to our requester.

Let's take a look at additional resources for Flask so we can continue to build on our API framework.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.71.115