Login system

UPDATE: We have now rebuilt our system to use NoSQL. 
The  below section no longer reflects the project. 
To view the updated project model, jump to the AROWF section directly.

I am going to build a simple WSGI interface in Python Flask for handling reviewer logins. I started learning the basics of Flask from the tutorial in the given link: http://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-i-hello-world. There’s a Flask Web Development book by the same blogger Miguel Grinberg. I personally found that more structured and up-to-date than the blog post. I built a simple social networking application by referring this book. I ‘ll now move onto building the actual login system. Here I have described steps to first install Flask.

Initial setup and installations:

I am installing pip3 since I have two versions of Python (Anaconda and 3.4) and I intend to use flask with python3.

sudo apt-get install python3-pip
sudo pip3 install virtualenv

Create a directory called wikireview. This will be the project directory and will host the entire codebase. Initialize a git repository here. You can refer to this article for help using git. Next, create a virtual environment called venv (you could give it any other name).

virtualenv venv

By now you would have had folder named flask that contains a complete Python environment ready to be used for this project.

Activate the virtual environment using:

source venv/bin/activate

Install all the dependencies that will be needed for building the login system.

$ pip install flask
$ pip install flask-script
$ pip install flask-bootstrap
$ pip install flask-moment
$ pip install flask-wtf
$ pip install flask-sqlalchemy
$ pip install flask-migrate
$ pip install flask-mail
$ pip install flask-login

Create a gitignore file using

$ touch .gitignore

Populate it with the contents of gitignore file here.

Commit 1 – Basic app structure

In the first commit, I created a basic structure of the app that looks something like this:

|-wikireview
    |-app/
        |-main
            |-__init__.py
            |-errors.py
            |-views.py
        |-static
            |-wikireview.ico
        |-templates/
            |-404.html
            |-500.html
            |-base.html
            |-index.html
        |-__init__.py
    |-config.py
    |-manage.py

I have not added the database initialization as yet. This will be done in a later commit.

This commit is just for setting up the basic app structure and displaying a welcome message on the screen when launched.

config.py – contains all the app configurations along with development, testing and production configuration settings

manage.py – launch script

app/__init__.py – app initializations and imports necessary blueprints

app/main/__init__.py – main blueprint

app/main/errors.py – view for rendering error pages, calls respective html pages

app/main/views.py – for calling index.html

app/static/wikireview.ico – icon for the site

app/templates/404.html – page not found error page

app/templates/500.html – error page for unhandled exceptions

app/templates/base.html – base html extending from bootstrap/base.html

app/templates/index.html – Home page, displays welcome message

The app can be tested by typing the following command:

$ python manage.py runserver

The code for this commit can be found here.

Commit 2 – Creating the database

In this commit I added the database design. For the login version, I have added just one table for reviewers. It contains id, email, username, password_hash, confirmed, agreement, and reputation. ‘id’ is automatically generated every time a new user registers. Since I ‘ll be using password hashing functionality from the Werkzeug package for security, only the encrypted password hashes will be stored in the database, not the actual passwords themselves. ‘confirmed’ column is a Boolean for the reviewer to confirm the account once he/she registers. ‘agreement’ and ‘reputation’ are floating points which range from 0-100 that hold the reviewer’s agreement and reputation score respectively. More will be discussed on these in the later sections.

The Reviewer model was stored in models.py. I have used the SQLAlchemy ORM for constructing the database. It uses a class based approach in creating SQLite databases. In the configuration settings,  I have specified three different databases which will be used in the development, testing and production stages. Now, we’ll be building the development database. For that execute the following commands:

$ python manage.py db init
$ python manage.py db migrate -m "initial migration"
$ python manage.py db upgrade

This should now create ‘data-dev.sqlite’ and a ‘migrations’ folder. The flask-migrate package is used for this purpose. You can think of it as a version control system for the database. Otherwise, every time we need to update a table, we would have to delete the earlier table. This is where flask-migrate comes in handy. It uses the underlying Alembic package written for SQLAlchemy, that keeps track of changes in the database.

I also modified manage.py to add make_shell_context that registers the application, db instances and models so that they are automatically imported into the shell.

You can now check if the app and database have been created using:

$ python manage.py shell
>>>app
<Flask 'app'>
>>>db
>>>SQLAlchemy engine='sqlite:////.../wikireview/data-dev.sqlite'>
>>>Reviewer
<class 'app.models.Reviewer'>
>>>Reviewer.query.all()
[]

The code for this commit can be found here.

Commit 3 –  Adding basic tests

I wrote test cases to check if the app  is created and is testing. These test cases were added in a file called test_basics.py inside a folder called tests. I also modified manage.py to create a test instance to run these tests.

You can run these tests by typing the following into the terminal:

$ python manage.py test

The code for this commit can be viewed here.

Commit 4 – Password hashing and authentication blueprint creation

I next began working on the authentication system. I used a package called Werkzeug for hashing the passwords. This is important because securely storing users’ passwords in the database is of utmost importance. Hashing functions take a password as input and apply cryptographic transformations to them so that only the passwords hashes are stored in the db and not he passwords themselves. I also wrote test cases to check for this functionality.

models.py – Implemented password hashing using Werkzeug

tests/test_reviewer_model.py – wrote test cases for password setting, denial for reading password directly, password verification, and checking for random password salts.

app/auth/__init__.py – Created an authentication blueprint

app/auth/views.py – view for rendering login.html

app/templates/auth/login.html – login page

app/__init__.py – added the auth blueprint

The code for this commit can be found here.

Commit 5 – User authentication with Flask-Login

I used the flask-login package for handling reviewer logins. Flask_login has the UserMixin class that has various methods that are of use in login systems. I implemented methods for signing users in and signing them out. While displaying html pages, it is necessary to protect the routes so that logged out users are not able to access certain pages. For this purpose, ‘current_user’ variable is defined by flask-login that contains the user that is currently logged in or a proxy anonymous user. In case of anonymous user, the is_authenticated method evaluates to False and prevent loading of protected pages.

app/__init__.py – instantiated login manager

app/models.py – added a callback method for loading a reviewer object

app/auth/forms.py – Added a login form for entering email and password

app/templates/base.html – Modified it to display ‘Login’ or ‘Logout’ in the navigation bar based on whether the user is logged in or not

app/auth/views.py – modified the login view so that it can display a customized page in case of valid user or displays login form again in case of incorrect credentials. Also added the logout view which logs out user and returns to home page.

app/templates/auth/login.html – loads the login form

app/templates/index.html – displays a customized web page for a logged in user

Make sure you set the environment variable ‘SECRET_KEY’ before checking out this version of the application. The environment variable can be set as:

$ export SECRET_KEY = enter-key-here

The code for this commit can be found here.

Commit 6 – New User Registration

app/auth/forms.py – added the registration form that takes in email, username, password1 and password2 (for matching). It also has methods for validating email and username.

app/templates/auth/register.html – registration template. Renders the registration form.

app/templates/auth/login.html – added a link to registration page for new users

app/auth/views.py – added the view for registration

The code for this commit can be found here.

Commit 7 – Sending confirmation mails

Many a times, it is important to verify if the user registering is sending in a valid mail id. For this purpose, I have added a confirmation mail sending functionality that sends a confirmation link to the email that the user registered with. The ‘confirmed’ attribute in the reviewer db is present for the same reason. Once the user clicks on the link in the email, this attribute is set to True and the reviewer can now begin reviewing items.

‘itsdangerous’ package provides cryptographically signed cookies to be used for generating confirmation tokens. These tokens are unique for each user and are constructed from the user id. They also have a timer which expires after a specified time (24 hours in our case).

For this task, I first had to implement email sending functionality. You need to set the MAIL_USERNAME and MAIL_PASSWORD environment variables prior to sending emails. You also need to be logged into your account and have turned on access for less secure apps in your Gmail account (in case you’re using Gmail for sending mails).

Once you’ve written email.py for sending asynchronous emails, you can check if the functionality in the shell using:

$ python manage.py shell
>>> from flask.ext.mail import Message
>>> from app import mail
>>> msg=Message('wikireview email testing', sender='admin@example.com', recipients=['email@example.com'])
>>> msg.body='success!'
>>> msg.html='<b>success!</b>'
>>> with app.app_context():
... mail.send(msg)

I then went on to write the plain text and html formats of the mail content.

app/models.py – used the Serializer object from itsdangerous to generate confirmation tokens

app/tests/test_reviewer_model.py – wrote tests for valid, invalid and expired confirmation token

app/email.py – wrote a script for sending asynchronous emails

app/auth/views.py – added views for confirmation page, resend confirmation mail page and necessary redirects for users

app/templates/auth/email/confirm.txt – plain text version of confirmation mail

app/templates/auth/email/confirm.html – html version of confirmation mail

app/templates/auth/unconfirmed.html – page for registered but unconfirmed users

The code for this commit can be found here.

Commit 8 – Updating passwords

This commit was aimed towards providing the reviewers with an option to update their passwords whenever they wished.

app/auth/forms.py – added a form for verifying old password and then ch$ange it

app/auth/views.py – added a view that renders the ChangePassword form and updates the db if valid password is entered or renders the form again in case of incorrect details

app/templates/auth/change-password.html – html for the page

app/templates/base.html – added a dropdown menu called account that has options to either log out or change your password

The code for this commit can be found here and here.

Commit 9 – Resetting passwords

In times when the user might forget his/her password, we need a secure and easy mechanism to reset the password. This is done via sending an email to the registered email address with a link to enter new password details. This link expires in an hour. All the modifications to the earlier code are:

app/auth/forms.py – added 2 forms – a reset request form and a reset form

app/auth/views.py – view for rendering the PasswordResetRequestForm. Once the email is validated, a mail is sent to this id with a link for resetting the password. There’s another view for the reset token link which renders the PasswordResetForm.

app/models.py – added two more functions for generating a reset token and resetting the password. The functionality is very similar to the functions used while sending confirmation mails.

app/templates/auth/email/reset_password.html

app/templates/auth/email/reset_password.txt

app/tempates/auth/login.html – added a ‘Forgot Password?’ option

app/templates/auth//reset_password.html – page that renders the reset request form

tests/test_reviewer_model.py – added two test cases for testing valid and invalid reset tokens

The code for this commit can be found here.

The login system is now complete and can be run in the server.

%d bloggers like this: