middleware.py 5.1 KB
Newer Older
1
"""Middleware for embargoing site and courses.
2 3 4 5 6 7

IMPORTANT NOTE: This code WILL NOT WORK if you have a misconfigured proxy
server.  If you are configuring embargo functionality, or if you are
experiencing mysterious problems with embargoing, please check that your
reverse proxy is setting any of the well known client IP address headers (ex.,
HTTP_X_FORWARDED_FOR).
8 9 10 11 12 13 14

This middleware allows you to:

* Embargoing courses (access restriction by courses)
* Embargoing site (access restriction of the main site)

Embargo can restrict by states and whitelist/blacklist (IP Addresses
15
(ie. 10.0.0.0), Networks (ie. 10.0.0.0/24)), or the user profile country.
16 17 18

Usage:

19
1) Enable embargo by setting `settings.FEATURES['EMBARGO']` to True.
20

21 22
2) In Django admin, create a new `IPFilter` model to block or whitelist
    an IP address from accessing the site.
23

24 25
3) In Django admin, create a new `RestrictedCourse` model and
    configure a whitelist or blacklist of countries for that course.
26

27
"""
28
import logging
29
import re
30

31
from django.core.exceptions import MiddlewareNotUsed
32
from django.core.urlresolvers import reverse
33
from django.conf import settings
34 35
from django.shortcuts import redirect
from ipware.ip import get_ip
36 37
from util.request import course_id_from_url

38 39
from .models import IPFilter
from . import api as embargo_api
40

41

42 43
log = logging.getLogger(__name__)

44 45

class EmbargoMiddleware(object):
46
    """Middleware for embargoing site and courses. """
47

48 49 50 51 52 53 54 55 56
    ALLOW_URL_PATTERNS = [
        # Don't block the embargo message pages; otherwise we'd
        # end up in an infinite redirect loop.
        re.compile(r'^/embargo/blocked-message/'),

        # Don't block the Django admin pages.  Otherwise, we might
        # accidentally lock ourselves out of Django admin
        # during testing.
        re.compile(r'^/admin/'),
57 58 59 60

        # Do not block access to course metadata. This information is needed for
        # sever-to-server calls.
        re.compile(r'^/api/course_structure/v[\d+]/courses/{}/$'.format(settings.COURSE_ID_PATTERN)),
61 62
    ]

63 64
    def __init__(self):
        # If embargoing is turned off, make this middleware do nothing
65
        if not settings.FEATURES.get('EMBARGO'):
66
            raise MiddlewareNotUsed()
67 68

    def process_request(self, request):
69 70
        """Block requests based on embargo rules.

71
        This will perform the following checks:
72 73 74 75 76 77 78 79 80

        1) If the user's IP address is blacklisted, block.
        2) If the user's IP address is whitelisted, allow.
        3) If the user's country (inferred from their IP address) is blocked for
            a courseware page, block.
        4) If the user's country (retrieved from the user's profile) is blocked
            for a courseware page, block.
        5) Allow access.

81
        """
82 83 84
        # Never block certain patterns by IP address
        for pattern in self.ALLOW_URL_PATTERNS:
            if pattern.match(request.path) is not None:
85
                return None
86

87 88
        ip_address = get_ip(request)
        ip_filter = IPFilter.current()
89

90 91 92 93 94 95
        if ip_filter.enabled and ip_address in ip_filter.blacklist_ips:
            log.info(
                (
                    u"User %s was blocked from accessing %s "
                    u"because IP address %s is blacklisted."
                ), request.user.id, request.path, ip_address
96 97
            )

98 99 100 101 102 103 104 105
            # If the IP is blacklisted, reject.
            # This applies to any request, not just courseware URLs.
            ip_blacklist_url = reverse(
                'embargo_blocked_message',
                kwargs={
                    'access_point': 'courseware',
                    'message_key': 'embargo'
                }
106
            )
107 108 109 110 111 112 113 114 115
            return redirect(ip_blacklist_url)

        elif ip_filter.enabled and ip_address in ip_filter.whitelist_ips:
            log.info(
                (
                    u"User %s was allowed access to %s because "
                    u"IP address %s is whitelisted."
                ),
                request.user.id, request.path, ip_address
116 117
            )

118 119 120
            # If the IP is whitelisted, then allow access,
            # skipping later checks.
            return None
121 122

        else:
123 124 125
            # Otherwise, perform the country access checks.
            # This applies only to courseware URLs.
            return self.country_access_rules(request.user, ip_address, request.path)
126

127
    def country_access_rules(self, user, ip_address, url_path):
128
        """
129 130 131
        Check the country access rules for a given course.
        Applies only to courseware URLs.

132
        Args:
133 134 135
            user (User): The user making the current request.
            ip_address (str): The IP address from which the request originated.
            url_path (str): The request path.
136

137 138
        Returns:
            HttpResponse or None
139 140

        """
141 142 143 144 145 146 147 148 149 150 151 152 153
        course_id = course_id_from_url(url_path)

        if course_id:
            redirect_url = embargo_api.redirect_if_blocked(
                course_id,
                user=user,
                ip_address=ip_address,
                url=url_path,
                access_point='courseware'
            )

            if redirect_url:
                return redirect(redirect_url)