Monday, April 8, 2019

Authentication

Authentication is an important aspect of any application. In our application only registered members can add products or reviews. We are going to use the built in authentication tools for Django. We will take the following steps:

  1. Create users and groups in Admin
  2. Add an include for django.auth in the project level urls
  3. Add a registration folder to templates
  4. Add a login template to the registration folder
  5. Create loginmessage and logoutmessage views
  6. Add templates for the login logout messages
  7. Register those views in the settings.py file
  8. Add links for login and logout to the base.html file
  9. Add the @login_required decorator to our form views
  10. Add tests for authenticated users

Create Users and Groups in Admin

Run the server and login to the admin site.

I am going to create a new group called "members."

Add and Include for django.auth

These are the permission I gave the group:

auth | user| can add user
auth | user| can change user
auth | user| can view user
techapp | product | can add product
techapp | product | can change product
techapp | product | can delete product
techapp | product | can view product
techapp | product type| can add product type
techapp | product type | can change product type
techapp | product type | can delete product type
techapp | product type | can view product type
techapp | review | can add review
techapp | review | can change review
techapp | review | can delete review
techapp | review | can view review

Now add a couple of users and add them to the member's group

Add an include in the project level urls.py

We need to add another include to our project level urls.py in order to find the pre-written authorization components.

urlpatterns = [
    path('admin/', admin.site.urls),
    path('techapp/', include('techapp.urls')),
    path('accounts/', include('django.contrib.auth.urls')),
]

Add a registration folder to templates

Now in VSCode right click on the templates folder and select "New Folder." Name the folder "registration." It must be spelled correctly and all in lower case.

Add login template to registration

In the registration folder we need to add our login template. It needs to be named "login.html."

{% extends 'base.html' %}
{% block content%}
<h2>Login</h2>
<form method="POST">
    {% csrf_token %}
    {{ form.as_p }}
    <button type="submit">login</button>
</form>

{% endblock %}

Create loginmessage and logoutmessage views

These views are like our original index view. They contain only a pointer to an html file.

def loginmessage(request):
    return render(request, 'techapp/loginmessage.html')

def logoutmessage(request):
    return render(request, 'techapp/logoutmessage.html')

We also, of course, have to register them in urls.py

urlpatterns=[
    path('', views.index, name='index'),
    path('gettypes/', views.gettypes, name='types'),
    path('getproducts/', views.getproducts, name='products'),
    path('productdetails/<int:id>', views.productdetails, name='productdetails'),
    path('newProduct/', views.newProduct, name='newproduct'),
    path('loginmessage/', views.loginmessage, name='loginmessage'),
    path('logoutmessage/', views.logoutmessage, name='logoutmessage'),
]

Create loginmessage and logout message templates

These templates go in the templates/techapp/ folder.

Here is the login message

{% extends 'base.html' %}
{% block content %}
<h2>Thank you</h2>
<p>Thank you for logging in.</p>
{% endblock%}

{% endblock %}

Here is the logout message.

{% extends 'base.html' %}
{% block content %}
<h2>Goodbye/h2>
<p>You are now logged out.</p>
{% endblock%}

Register view in settings.py

There is one last place we need to register the loginmessage and logoutmessage. We need to add a reference to them at the very bottom of the settings.py file, right after the "STATIC_URL" statement.

LOGIN_REDIRECT_URL='loginmessage'
LOGOUT_REDIRECT_URL='logoutmessage'

Add the @login_required decorator

In views.py we need to imports the login_required decorators.

from django.contrib.auth.decorators import login_required'

Now we add the "decorator" to the views that require a login.

@login_required
def newProduct(request):
     form=ProductForm
     if request.method=='POST':
          form=ProductForm(request.POST)
          if form.is_valid():
               post=form.save(commit=True)
               post.save()
               form=ProductForm()
     else:
          form=ProductForm()
     return render(request, 'techapp/newproduct.html', {'form': form})

This simple addition makes it so that if a user is not logged it, the program will throw them to the login form.

Add links in base,html

Lastly we add login and logout links to the base.html file.

<li><a href="{% url 'login' %}">Login</a></li>
<li><a href="{% url 'logout' %}">Logout</a></li>

Run the server to test the new login, logout and decorator. When I click on the menu item to add a product, I am thrown to the login:

After I login I can enter form data:

When I logout I get the logout message.

Add tests for authenticated users

Finally we can add some unit tests to check if the authentication works properly. First we set up a user, a Producttype and a Product. The first tests checks to make sure the program redirects to the login form in no user is logged in. The second test actually contains three tests: We log in a user, then test to make sure the user can access the form. We make sure the status is OK and that it is using the correct template.

class New_Product_authentication_test(TestCase):
    def setUp(self):
        self.test_user=User.objects.create_user(username='testuser1', password='P@ssw0rd1')
        self.type=ProductType.objects.create(typename='laptop')
        self.prod = Product.objects.create(productname='product1', producttype=self.type, user=self.test_user, productprice=500, productentrydate='2019-04-02',producturl= 'http://www.dell.com', productdescription="a product")

    def test_redirect_if_not_logged_in(self):
        response=self.client.get(reverse('newproduct'))
        self.assertRedirects(response, '/accounts/login/?next=/techapp/newProduct/')

    def test_Logged_in_uses_correct_template(self):
        login=self.client.login(username='testuser1', password='P@ssw0rd1')
        response=self.client.get(reverse('newproduct'))
        self.assertEqual(str(response.context['user']), 'testuser1')
        self.assertEqual(response.status_code, 200)
        self.assertTemplateUsed(response, 'techapp/newproduct.html')

No comments:

Post a Comment