PK=5JuɰAAiprestrict/admin.py# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.contrib import admin from django import forms import re from . import ip_utils as ipu from . import models from .geoip import is_valid_country_code NOT_LETTER = re.compile(r'[^A-Z]+') @admin.register(models.Rule) class RuleAdmin(admin.ModelAdmin): exclude = ('rank',) list_display = ('url_pattern', 'ip_group', 'reverse_ip_group', 'is_allowed', 'move_up_url', 'move_down_url') class IPRangeForm(forms.ModelForm): class Meta: model = models.IPRange exclude = () def clean(self): cleaned_data = super(IPRangeForm, self).clean() first_ip = cleaned_data.get('first_ip') if first_ip is None: # first_ip is Mandatory, so just let the default validator catch this return cleaned_data version = ipu.get_version(first_ip) last_ip = cleaned_data['last_ip'] cidr = cleaned_data.get('cidr_prefix_length') if cidr is not None: if version == ipu.IPv4 and not (1 <= cidr <= 31): self.add_error('cidr_prefix_length', 'Must be a number between 1 and 31') return cleaned_data if version == ipu.IPv6 and not (1 <= cidr <= 127): self.add_error('cidr_prefix_length', 'Must be a number between 1 and 127') return cleaned_data if last_ip and cidr: raise forms.ValidationError("Don't specify the Last IP if you specified a CIDR prefix length") if last_ip: if version != ipu.get_version(last_ip): raise forms.ValidationError( "Last IP should be the same type as First IP (%s)" % version) if ipu.to_number(first_ip) > ipu.to_number(last_ip): raise forms.ValidationError("Last IP should be greater than First IP") if cidr: # With CIDR the starting address could be different than the one # the user specified. Making sure it is set to the first ip in the # subnet. start, end = ipu.cidr_to_range(first_ip, cidr) cleaned_data['first_ip'] = ipu.to_ip(start, version=version) return cleaned_data class IPRangeInline(admin.TabularInline): model = models.IPRange form = IPRangeForm fields = ['first_ip', 'cidr_prefix_length', 'last_ip', 'ip_type'] readonly_fields = ['ip_type'] extra = 2 class IPLocationForm(forms.ModelForm): class Meta: model = models.IPLocation exclude = () def clean_country_codes(self): codes = self.cleaned_data['country_codes'] codes = set(filter(lambda x: x != '', NOT_LETTER.split(self.cleaned_data['country_codes'].upper()))) if not all(map(is_valid_country_code, codes)): incorrect = [c for c in codes if not is_valid_country_code(c)] msg = ('""%s" must be a valid country code' if len(incorrect) == 1 else '""%s" must be valid country codes') raise forms.ValidationError(msg % ', '.join(incorrect)) codes = list(codes) codes.sort() return ', '.join(codes) class IPLocationInline(admin.TabularInline): model = models.IPLocation form = IPLocationForm fields = ['country_codes'] extra = 2 @admin.register(models.RangeBasedIPGroup) class RangeBasedIPGroupAdmin(admin.ModelAdmin): exclude = ('type',) inlines = [IPRangeInline] @admin.register(models.LocationBasedIPGroup) class LocationBasedIPGroupAdmin(admin.ModelAdmin): exclude = ('type',) inlines = [IPLocationInline] PK>Jviprestrict/__init__.py# -*- coding: utf-8 -*- from __future__ import unicode_literals from .restrictor import IPRestrictor __all__ = ["IPRestrictor"] __version__ = "1.3.0" PK=5JJ  iprestrict/models.py# -*- coding: utf-8 -*- from __future__ import unicode_literals import re from django.core.urlresolvers import reverse from django.db import models from django.utils import timezone from . import ip_utils as ipu from .geoip import get_geoip, NO_COUNTRY TYPE_LOCATION = 'location' TYPE_RANGE = 'range' geoip = get_geoip() class IPGroupManager(models.Manager): def get_queryset(self): qs = super(IPGroupManager, self).get_queryset() if self.model.TYPE is not None: return qs.filter(type=self.model.TYPE) return qs class IPGroup(models.Model): TYPE_CHOICES = ((TYPE_LOCATION, 'Location based'), (TYPE_RANGE, 'Range based')) TYPE = None name = models.CharField(max_length=100) description = models.TextField(null=True, blank=True) type = models.CharField(max_length=10, default=TYPE_RANGE, choices=TYPE_CHOICES) class Meta: verbose_name = 'IP Group' objects = IPGroupManager() def __init__(self, *args, **kwargs): super(IPGroup, self).__init__(*args, **kwargs) self.load() def load(self): pass def save(self, *args, **kwargs): self.type = self.TYPE super(IPGroup, self).save(*args, **kwargs) def __str__(self): return self.name __unicode__ = __str__ def typed_ip_group(ip_group): obj = None if ip_group.type == TYPE_RANGE: obj = RangeBasedIPGroup(pk=ip_group.pk) elif ip_group.type == TYPE_LOCATION: obj = LocationBasedIPGroup(pk=ip_group.pk) else: raise ValueError("Invalid type '%s'" % ip_group.type) obj.__dict__.update(ip_group.__dict__) return obj class RangeBasedIPGroup(IPGroup): TYPE = TYPE_RANGE class Meta: proxy = True verbose_name = 'IP Group' def load_ranges(self): self._ranges = {ipu.IPv4: [], ipu.IPv6: []} for r in self.iprange_set.all(): self._ranges[r.ip_type].append(r) load = load_ranges def ranges(self, ip_type=None): if ip_type is None: return self._ranges[ipu.IPv4] + self._ranges[ipu.IPv6] return self._ranges[ip_type] def matches(self, ip): ip_type = ipu.get_version(ip) for r in self.ranges(ip_type): if ip in r: return True return False def details_str(self): return ', '.join([str(r) for r in self.ranges()]) class LocationBasedIPGroup(IPGroup): TYPE = TYPE_LOCATION class Meta: proxy = True verbose_name = 'Location Based IP Group' def load_locations(self): countries = ", ".join(self.iplocation_set.values_list('country_codes', flat=True)).split(', ') countries.sort() self._countries = ', '.join(countries) load = load_locations def matches(self, ip): country_code = geoip.country_code(ip) or NO_COUNTRY return country_code in self._countries def details_str(self): return self._countries class IPRange(models.Model): class Meta: verbose_name = "IP Range" ip_group = models.ForeignKey(IPGroup) first_ip = models.GenericIPAddressField() cidr_prefix_length = models.PositiveSmallIntegerField(null=True, blank=True) last_ip = models.GenericIPAddressField(null=True, blank=True) @property def start(self): if self.cidr_prefix_length is not None: start, end = ipu.cidr_to_range(self.first_ip, self.cidr_prefix_length) return start else: return ipu.to_number(self.first_ip) @property def end(self): if self.last_ip is not None: return ipu.to_number(self.last_ip) if self.cidr_prefix_length is not None: start, end = ipu.cidr_to_range(self.first_ip, self.cidr_prefix_length) return end return self.start @property def ip_type(self): if not self.first_ip: return '' return ipu.get_version(self.first_ip) def __contains__(self, ip): ip_nr = ipu.to_number(ip) return self.start <= ip_nr <= self.end def __str__(self): result = str(self.first_ip) if self.cidr_prefix_length is not None: result += '/' + str(self.cidr_prefix_length) elif self.last_ip is not None: result += '-' + str(self.last_ip) return result __unicode__ = __str__ class IPLocation(models.Model): class Meta: verbose_name = "IP Location" ip_group = models.ForeignKey(IPGroup) country_codes = models.CharField(max_length=2000, help_text='Comma-separated list of 2 character country codes') def __contains__(self, country_code): return country_code in re.split(r'[^A-Z]+', self.country_codes) def __str__(self): return self.country_codes __unicode__ = __str__ class Rule(models.Model): class Meta: ordering = ['rank', 'id'] ACTION_CHOICES = ( ('A', 'ALLOW'), ('D', 'DENY') ) url_pattern = models.CharField(max_length=500) ip_group = models.ForeignKey(IPGroup, default=1) reverse_ip_group = models.BooleanField(default=False) action = models.CharField(max_length=1, choices=ACTION_CHOICES, default='D') rank = models.IntegerField(blank=True) def __init__(self, *args, **kwargs): super(Rule, self).__init__(*args, **kwargs) self.ip_group = typed_ip_group(self.ip_group) @property def regex(self): if not hasattr(self, '_regex'): self._regex = re.compile(self.url_pattern) return self._regex def matches_url(self, url): if self.url_pattern == 'ALL': return True else: return self.regex.match(url) is not None def matches_ip(self, ip): match = typed_ip_group(self.ip_group).matches(ip) if self.reverse_ip_group: return not match return match def is_restricted(self): return self.action != 'A' def is_allowed(self): return self.action == 'A' is_allowed.boolean = True is_allowed.short_description = 'Is allowed?' def action_str(self): return 'Allowed' if self.is_allowed() else 'Denied' def swap_with_rule(self, other): other.rank, self.rank = self.rank, other.rank other.save() self.save() def move_up(self): rules_above = Rule.objects.filter(rank__lt=self.rank).order_by('-rank') if len(rules_above) == 0: return self.swap_with_rule(rules_above[0]) def move_up_url(self): url = reverse('iprestrict:move_rule_up', args=[self.pk]) return 'Move Up' % url move_up_url.allow_tags = True move_up_url.short_description = 'Move Up' def move_down_url(self): url = reverse('iprestrict:move_rule_down', args=[self.pk]) return 'Move Down' % url move_down_url.allow_tags = True move_down_url.short_description = 'Move Down' def move_down(self): rules_below = Rule.objects.filter(rank__gt=self.rank) if len(rules_below) == 0: return self.swap_with_rule(rules_below[0]) def save(self, *args, **kwargs): if self.rank is None: max_aggr = Rule.objects.filter(rank__lt=65000).aggregate(models.Max('rank')) max_rank = max_aggr.get('rank__max') if max_rank is None: max_rank = 0 self.rank = max_rank + 1 super(Rule, self).save(*args, **kwargs) class ReloadRulesRequest(models.Model): at = models.DateTimeField(auto_now_add=True) @classmethod def request_reload(cls): rrs = ReloadRulesRequest.objects.all() if len(rrs) > 0: obj = rrs[0] obj.at = timezone.now() obj.save() else: cls.objects.create() @staticmethod def last_request(): result = None rrs = ReloadRulesRequest.objects.all() if len(rrs) > 0: result = rrs[0].at return result PK=5J x iprestrict/views.py# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.core.exceptions import ValidationError from django.core.urlresolvers import reverse from django.core.validators import validate_ipv46_address from django.http import HttpResponse, HttpResponseRedirect from django.shortcuts import render_to_response import json from . import models from .decorators import superuser_required @superuser_required def move_rule_up(request, rule_id): rule = models.Rule.objects.get(pk=rule_id) rule.move_up() return HttpResponseRedirect(reverse('admin:iprestrict_rule_changelist')) @superuser_required def move_rule_down(request, rule_id): rule = models.Rule.objects.get(pk=rule_id) rule.move_down() return HttpResponseRedirect(reverse('admin:iprestrict_rule_changelist')) @superuser_required def reload_rules(request): models.ReloadRulesRequest.request_reload() return HttpResponse('ok') @superuser_required def test_rules_page(request): return render_to_response('iprestrict/test_rules.html') @superuser_required def test_match(request): request_dict = request.GET if request.method == 'POST': request_dict = request.POST url = request_dict['url'] ip = request_dict['ip'] try: validate_ipv46_address(ip) except ValidationError: return HttpResponse(_test_match_result('Error', msg='Invalid IP address')) matching_rule_id, action = find_matching_rule(url, ip) rules = list_rules(matching_rule_id, url, ip) if matching_rule_id is None: action = 'Allowed' msg = 'No rules matched.' else: msg = 'URL matched Rule highlighted below.' return HttpResponse(_test_match_result(action, msg, rules)) def _test_match_result(action, msg=None, rules=None): return json.dumps({ 'action': action, 'msg': msg or '', 'rules': rules or [], }) def find_matching_rule(url, ip): for r in models.Rule.objects.all(): if r.matches_url(url) and r.matches_ip(ip): return r.pk, r.action_str() return None, None def list_rules(matching_rule_id, url, ip): return [map_rule(r, matching_rule_id, url, ip) for r in models.Rule.objects.all()] def map_rule(r, matching_rule_id, url, ip): rule = { 'url_pattern': { 'value': r.url_pattern, 'matchStatus': 'match' if r.matches_url(url) else 'noMatch' }, 'ip_group': { 'name': r.ip_group.name, 'reverse_ip_group': 'NOT' if r.reverse_ip_group else '', 'ranges': r.ip_group.details_str(), 'matchStatus': 'match' if r.matches_ip(ip) else 'noMatch' }, 'action': r.action_str(), } if r.pk == matching_rule_id: rule['matched'] = True return rule PK=5JÜiprestrict/restrictor.py# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.utils import timezone class IPRestrictor(object): rules = None last_reload = None def __init__(self): self.load_rules() def is_restricted(self, url, ip): for rule in self.rules: if rule.matches_url(url) and rule.matches_ip(ip): return rule.is_restricted() return False def load_rules(self): # We are caching the rules, to avoid DB lookup on each request from .models import Rule self.rules = list(Rule.objects.all()) self.last_reload = timezone.now() reload_rules = load_rules PK=5JJIIiprestrict/urls.py# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.conf.urls import url from .views import test_rules_page, test_match, reload_rules from .views import move_rule_up, move_rule_down app_name = "iprestrict" urlpatterns = [ url(r'^$', test_rules_page), url(r'^move_rule_up/(?P\d+)[/]?$', move_rule_up, name='move_rule_up'), url(r'^move_rule_down/(?P\d+)[/]?$', move_rule_down, name='move_rule_down'), url(r'^reload_rules[/]?$', reload_rules, name='reload_rules'), url(r'^test_match[/]?$', test_match, name='test_match'), ] PK=5J?iprestrict/decorators.py# -*- coding: utf-8 -*- from __future__ import unicode_literals from functools import wraps from django.utils.translation import ugettext as _ from django.contrib.admin.forms import AdminAuthenticationForm from django.contrib.auth.views import login from django.contrib.auth import REDIRECT_FIELD_NAME from django.http import HttpResponseForbidden # Copied from django staff_member_required # Why isn't this provided by Django? def superuser_required(view_func): """ Decorator for views that checks that the user is logged in and is a staff member, displaying the login page if necessary. """ @wraps(view_func) def _checklogin(request, *args, **kwargs): if request.user.is_active and request.user.is_superuser: # The user is valid. Continue to the admin page. return view_func(request, *args, **kwargs) if request.user.is_authenticated(): return HttpResponseForbidden('Forbidden!') assert hasattr(request, 'session'), ("The Django admin requires session middleware to be installed. " "Edit your MIDDLEWARE_CLASSES setting to insert " "'django.contrib.sessions.middleware.SessionMiddleware'.") defaults = { 'template_name': 'admin/login.html', 'authentication_form': AdminAuthenticationForm, 'extra_context': { 'title': _('Log in'), 'app_path': request.get_full_path(), REDIRECT_FIELD_NAME: request.get_full_path(), }, } return login(request, **defaults) return _checklogin PK=5JN iprestrict/middleware.py# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.core import exceptions from django.conf import settings import logging import warnings from .models import ReloadRulesRequest from .restrictor import IPRestrictor try: from django.utils.deprecation import MiddlewareMixin except ImportError: class MiddlewareMixin(object): def __init__(self, *args, **kwargs): pass logger = logging.getLogger(__name__) class IPRestrictMiddleware(MiddlewareMixin): restrictor = None trusted_proxies = None allow_proxies = None reload_rules = None def __init__(self, *args, **kwargs): super(IPRestrictMiddleware, self).__init__(*args, **kwargs) self.restrictor = IPRestrictor() self.trusted_proxies = tuple(get_setting('IPRESTRICT_TRUSTED_PROXIES', 'TRUSTED_PROXIES', [])) self.reload_rules = get_reload_rules_setting() self.ignore_proxy_header = bool(get_setting('IPRESTRICT_IGNORE_PROXY_HEADER', 'IGNORE_PROXY_HEADER', False)) def process_request(self, request): if self.reload_rules: self.reload_rules_if_needed() url = request.path_info client_ip = self.extract_client_ip(request) if self.restrictor.is_restricted(url, client_ip): logger.warn("Denying access of %s to %s" % (url, client_ip)) raise exceptions.PermissionDenied def extract_client_ip(self, request): client_ip = request.META['REMOTE_ADDR'] if not self.ignore_proxy_header: forwarded_for = self.get_forwarded_for(request) if forwarded_for: closest_proxy = client_ip client_ip = forwarded_for.pop(0) proxies = [closest_proxy] + forwarded_for for proxy in proxies: if proxy not in self.trusted_proxies: logger.warn("Client IP %s forwarded by untrusted proxy %s" % (client_ip, proxy)) raise exceptions.PermissionDenied return client_ip def get_forwarded_for(self, request): hdr = request.META.get('HTTP_X_FORWARDED_FOR') if hdr is not None: return [ip.strip() for ip in hdr.split(',')] else: return [] def reload_rules_if_needed(self): last_reload_request = ReloadRulesRequest.last_request() if last_reload_request is not None: if self.restrictor.last_reload < last_reload_request: self.restrictor.reload_rules() def get_setting(new_name, old_name, default=None): setting_name = new_name if hasattr(settings, old_name): setting_name = old_name warn_about_changed_setting(old_name, new_name) return getattr(settings, setting_name, default) def get_reload_rules_setting(): if hasattr(settings, 'DONT_RELOAD_RULES'): warn_about_changed_setting('DONT_RELOAD_RULES', 'IPRESTRICT_RELOAD_RULES') return not bool(getattr(settings, 'DONT_RELOAD_RULES')) return bool(getattr(settings, 'IPRESTRICT_RELOAD_RULES', True)) def warn_about_changed_setting(old_name, new_name): # DeprecationWarnings are ignored by default, so lets make sure that # the warnings are shown by using the default UserWarning instead warnings.warn("The setting name '%s' has been deprecated and it will be removed in a future version. " "Please use '%s' instead." % (old_name, new_name)) PK=5J~P P iprestrict/ip_utils.py# -*- coding: utf-8 -*- from __future__ import unicode_literals IPv4 = 'ipv4' IPv6 = 'ipv6' def get_version(ip): return IPv6 if ':' in ip else IPv4 def is_ipv4(ip): return get_version(ip) == IPv4 def is_ipv6(ip): return get_version(ip) == IPv6 def to_number(ip): return ipv6_to_number(ip) if is_ipv6(ip) else ipv4_to_number(ip) def ipv4_to_number(ip): return _ip_to_number(ip) def ipv6_to_number(ip): if '.' in ip: ip = convert_mixed(ip) if '::' in ip: ip = explode(ip) return _ip_to_number(ip, separator=':', group_size=2 ** 16, base=16) def explode(ip): if ip.count('::') > 1: raise ValueError('Invalid ip address "%s". "::" can appear only once.' % ip) pre, post = [reject_empty(x.split(':')) for x in ip.split('::')] return ':'.join(pre + ['0'] * (8 - len(pre) - len(post)) + post) def convert_mixed(ip): last_colon = ip.rfind(':') ipv6, ipv4 = ip[:last_colon+1], ip[last_colon+1:] if ipv4.count('.') != 3: raise ValueError('Invalid IPv6 address "%s". Dotted ipv4 part should be at the end.' % ip) a, b, c, d = ipv4.split('.') pre_last = 256 * int(a) + int(b) last = 256 * int(c) + int(d) return '%s:%x:%x' % (ipv6, pre_last, last) def to_ip(number, version=IPv4): if version == IPv6: separator = ':' parts_count = 8 parts_length = 16 fmt = '%x' else: separator = '.' parts_count = 4 parts_length = 8 fmt = '%d' mask = int('1' * parts_length, 2) parts = [] for i in range(parts_count): shifted_number = number >> (parts_length * i) parts.append(shifted_number & mask) return separator.join(map(lambda i: fmt % i, reversed(parts))) def cidr_to_range(ip, prefix_length): ip_length = 128 if is_ipv6(ip) else 32 ip = to_number(ip) start_mask = int('1' * prefix_length, 2) << (ip_length - prefix_length) end_mask = int('1' * (ip_length - prefix_length), 2) start = ip & start_mask end = start | end_mask return (start, end) def _ip_to_number(ip, separator='.', group_size=2 ** 8, base=10): parts = ip.split(separator) parts = [int(p, base) for p in reversed(parts)] nr = 0 for i, d in enumerate(parts): nr += (group_size ** i) * d return nr def reject_empty(l): return [x for x in l if x] PK>J7w;. . iprestrict/geoip.py# -*- coding: utf-8 -*- '''Client-side wrapper lib around GeoIP. By using this module we allow: - if GeoIP2 is unavailable (pre Django 1.9) fall back on GeoIP - make the geoip module optional. If the user opts out we don't fail on importing required modules ''' from __future__ import unicode_literals from django.core.exceptions import ImproperlyConfigured from django.conf import settings available_geoip = 2 try: from django.contrib.gis.geoip2 import GeoIP2 from geoip2.errors import AddressNotFoundError except ImportError: available_geoip = 1 try: from django.contrib.gis.geoip import GeoIP except ImportError: available_geoip = None try: from pycountry import countries except ImportError: if getattr(settings, 'IPRESTRICT_GEOIP_ENABLED', True): raise ImproperlyConfigured( "You are using location based IP Groups, but the python package " "pycountry isn't installed. Please install pycountry or set 'IPRESTRICT_GEOIP_ENABLED' " "to False in settings.py") # Special value for IP addresses that have no country like localhost. # Using the 'XX' special value allows for rules being set up on the 'XX' country code # and giving more control to end-users on what to do for special cases like this NO_COUNTRY = 'XX' class AdaptedGeoIP2(object): '''Makes GeoIP2 behave like GeoIP''' def __init__(self, *args, **kwargs): self._geoip = GeoIP2() def country_code(self, ip): # if the IP isn't in the DB return None instead of throwing an Exception as GeoIP does try: return self._geoip.country_code(ip) except AddressNotFoundError: return None class OurGeoIP(object): def country_code(self, ip): raise ImproperlyConfigured( "You are using location based IP Groups, " "but 'IPRESTRICT_GEOIP_ENABLED' isn't set to True in settings.py") _geoip = OurGeoIP() if getattr(settings, 'IPRESTRICT_GEOIP_ENABLED', True): if available_geoip is None: raise ImproperlyConfigured( "'IPRESTRICT_GEOIP_ENABLED' is set to True, but neither geoip nor geoip2 is available " " to import. Make sure the geoip libraries are installed as described in the Django " "documentation") _geoip = AdaptedGeoIP2() if available_geoip == 2 else GeoIP() def get_geoip(): return _geoip def is_valid_country_code(code): if code == NO_COUNTRY: return True try: countries.get(alpha_2=code) return True except KeyError: return False PK=5JѪO+0iprestrict/migrations/0006_auto_20161013_1327.py# -*- coding: utf-8 -*- # Generated by Django 1.10.2 on 2016-10-13 13:27 from __future__ import unicode_literals from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ ('iprestrict', '0005_add_reverse_ipgroup'), ] operations = [ migrations.AlterField( model_name='ipgroup', name='type', field=models.CharField(choices=[('location', 'Location based'), ('range', 'Range based')], default='range', max_length=10), ), migrations.AlterField( model_name='iplocation', name='country_codes', field=models.CharField(help_text='Comma-separated list of 2 character country codes', max_length=2000), ), migrations.AlterField( model_name='rule', name='action', field=models.CharField(choices=[('A', 'ALLOW'), ('D', 'DENY')], default='D', max_length=1), ), ] PK=5Jk5{v1iprestrict/migrations/0005_add_reverse_ipgroup.py# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ ('iprestrict', '0004_add_iplocation'), ] operations = [ migrations.AlterModelOptions( name='ipgroup', options={'verbose_name': 'IP Group'}, ), migrations.AddField( model_name='rule', name='reverse_ip_group', field=models.BooleanField(default=False), ), ] PK=5J#tt!iprestrict/migrations/__init__.py# -*- coding: utf-8 -*- from __future__ import ( absolute_import, division, print_function, unicode_literals ) PK%5HgsNN*iprestrict/migrations/0002_initial_data.py# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import migrations def create_data(apps, schema_editor): IPGroup = apps.get_model('iprestrict', 'IPGroup') IPRange = apps.get_model("iprestrict", "IPRange") Rule = apps.get_model("iprestrict", "Rule") # Default IP Groups: ALL and localhost all_group = IPGroup.objects.create( name='ALL', description='Matches all IP Addresses') localhost_group = IPGroup.objects.create( name='localhost', description='IP address of localhost') # IP ranges defining ALL and localhost for ipv4 and ipv6 IPRange.objects.create( ip_group=all_group, first_ip='0.0.0.0', last_ip='255.255.255.255') IPRange.objects.create( ip_group=all_group, first_ip='0:0:0:0:0:0:0:0', last_ip='ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff') IPRange.objects.create( ip_group=localhost_group, first_ip='127.0.0.1', last_ip=None) IPRange.objects.create( ip_group=localhost_group, first_ip='::1', last_ip=None) # Default rules: Allow all for localhost and Deny everything else Rule.objects.create( ip_group=localhost_group, action='A', url_pattern='ALL', rank=65535) Rule.objects.create( ip_group=all_group, action='D', url_pattern='ALL', rank=65536) class Migration(migrations.Migration): dependencies = [ ('iprestrict', '0001_initial'), ] operations = [ migrations.RunPython(create_data), ] PK=5J,5>>,iprestrict/migrations/0004_add_iplocation.py# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ ('iprestrict', '0003_add_ipgroup_types'), ] operations = [ migrations.CreateModel( name='IPLocation', fields=[ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ('country_codes', models.CharField(help_text=b'Comma-separated list of 2 character country codes', max_length=2000)), ], options={ 'verbose_name': 'IP Location', }, ), migrations.AlterModelOptions( name='ipgroup', options={}, ), migrations.AlterModelOptions( name='rangebasedipgroup', options={'verbose_name': 'IP Group'}, ), migrations.AlterField( model_name='ipgroup', name='type', field=models.CharField(default=b'range', max_length=10, choices=[(b'location', b'Location based'), (b'range', b'Range based')]), ), migrations.AddField( model_name='iplocation', name='ip_group', field=models.ForeignKey(to='iprestrict.IPGroup'), ), ] PK=5J/iprestrict/migrations/0003_add_ipgroup_types.py# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ ('iprestrict', '0002_initial_data'), ] operations = [ migrations.CreateModel( name='LocationBasedIPGroup', fields=[ ], options={ 'verbose_name': 'Location Based IP Group', 'proxy': True, }, bases=('iprestrict.ipgroup',), ), migrations.CreateModel( name='RangeBasedIPGroup', fields=[ ], options={ 'proxy': True, }, bases=('iprestrict.ipgroup',), ), migrations.AddField( model_name='ipgroup', name='type', field=models.CharField(default=b'range', max_length=10, choices=[(b'location', b'location based'), (b'range', b'range based')]), ), ] PK%5H=9%iprestrict/migrations/0001_initial.py# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ ] operations = [ migrations.CreateModel( name='IPGroup', fields=[ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ('name', models.CharField(max_length=100)), ('description', models.TextField(null=True, blank=True)), ], options={ 'verbose_name': 'IP Group', }, ), migrations.CreateModel( name='IPRange', fields=[ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ('first_ip', models.GenericIPAddressField()), ('cidr_prefix_length', models.PositiveSmallIntegerField(null=True, blank=True)), ('last_ip', models.GenericIPAddressField(null=True, blank=True)), ('ip_group', models.ForeignKey(to='iprestrict.IPGroup')), ], options={ 'verbose_name': 'IP Range', }, ), migrations.CreateModel( name='ReloadRulesRequest', fields=[ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ('at', models.DateTimeField(auto_now_add=True)), ], ), migrations.CreateModel( name='Rule', fields=[ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ('url_pattern', models.CharField(max_length=500)), ('action', models.CharField(default=b'D', max_length=1, choices=[(b'A', b'ALLOW'), (b'D', b'DENY')])), ('rank', models.IntegerField(blank=True)), ('ip_group', models.ForeignKey(default=1, to='iprestrict.IPGroup')), ], options={ 'ordering': ['rank', 'id'], }, ), ] PK%5H!iprestrict/management/__init__.pyPK=5J .iprestrict/management/commands/reload_rules.py# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.core.management.base import BaseCommand, CommandError from ... import models from ...middleware import get_reload_rules_setting class Command(BaseCommand): help = 'Requests the reload of the ip restriction rules from the DB.' def handle(self, *args, **options): verbosity = int(options.get('verbosity', '1')) reload_rules = get_reload_rules_setting() if not reload_rules: raise CommandError("IPRESTRICT_RELOAD_RULES is set to False. " "Your IPRestrict rules can't be changed dynamically.") models.ReloadRulesRequest.request_reload() if verbosity >= 1: self.stdout.write('Successfully requested reload of rules') PK%5H*iprestrict/management/commands/__init__.pyPK=5Jo-tSS-iprestrict/management/commands/importrules.py# -*- coding: utf-8 -*- from __future__ import unicode_literals from ._utils import warn_about_renamed_command from . import import_rules class Command(import_rules.Command): def handle(self, *args, **options): warn_about_renamed_command('importrules', 'import_rules') super(Command, self).handle(*args, **options) PK=5JA'b@@.iprestrict/management/commands/import_rules.py# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.core.management.base import BaseCommand from django.core.management import call_command from django.db import transaction from ... import models class Command(BaseCommand): help = 'Replaces the current rules in the DB with the rules in the given fixture file(s).' def add_arguments(self, parser): parser.add_argument('fixture', nargs='+') def handle(self, *args, **options): fixtures = options.get('fixture', []) with transaction.atomic(): self.delete_existing_rules() call_command('loaddata', *fixtures, **options) def delete_existing_rules(self): models.Rule.objects.all().delete() models.IPRange.objects.all().delete() models.IPGroup.objects.all().delete() PK=5JvSS-iprestrict/management/commands/reloadrules.py# -*- coding: utf-8 -*- from __future__ import unicode_literals from ._utils import warn_about_renamed_command from . import reload_rules class Command(reload_rules.Command): def handle(self, *args, **options): warn_about_renamed_command('reloadrules', 'reload_rules') super(Command, self).handle(*args, **options) PK=5Jn(iprestrict/management/commands/_utils.pyimport warnings def warn_about_renamed_command(old_name, new_name): # DeprecationWarnings are ignored by default, so lets make sure that # the warnings are shown by using the default UserWarning instead warnings.warn("The command '%s' has been deprecated and it will be removed in a future version. " "Please use '%s' instead." % (old_name, new_name)) PK=5JD@YY1iprestrict/management/commands/add_ip_to_group.py# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.core.exceptions import ValidationError from django.core.management.base import BaseCommand, CommandError from django.core.validators import validate_ipv46_address from ... import models class Command(BaseCommand): help = 'Adds an IP address to an IP Group.' def add_arguments(self, parser): parser.add_argument('group_name') parser.add_argument('ip', nargs='+') def handle(self, *args, **options): group_name = options.get('group_name') ips = options.get('ip') try: ip_group = models.IPGroup.objects.get(name__iexact=group_name, type=models.TYPE_RANGE) except models.IPGroup.DoesNotExist: try: models.IPGroup.objects.get(name__iexact=group_name) except models.IPGroup.DoesNotExist: raise CommandError("IPGroup '%s' doesn't exist." % group_name) else: raise CommandError("Can add IP address only to a Range based IP Group.") for ip in ips: try: validate_ipv46_address(ip) except ValidationError: raise CommandError("'%s' not a valid IPv4 or IPv6 address." % ip) for ip in ips: models.IPRange.objects.get_or_create(ip_group=ip_group, first_ip=ip) PKS@J'ܞhh1django_iprestrict-1.3.0.dist-info/DESCRIPTION.rstDjango app + middleware to restrict access to all or sections of a Django project by client IP ranges PKS@JwAKK/django_iprestrict-1.3.0.dist-info/metadata.json{"classifiers": ["Framework :: Django", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Topic :: Software Development"], "download_url": "https://github.com/muccg/django-iprestrict/releases", "extensions": {"python.details": {"contacts": [{"email": "devops@ccg.murdoch.edu.au", "name": "Tamas Szabo, CCG, Murdoch University", "role": "author"}], "document_names": {"description": "DESCRIPTION.rst"}, "project_urls": {"Home": "https://github.com/muccg/django-iprestrict"}}}, "extras": ["dev", "geoip"], "generator": "bdist_wheel (0.26.0)", "metadata_version": "2.0", "name": "django-iprestrict", "run_requires": [{"requires": ["Django (>=1.8)", "django-templatetag-handlebars (==1.3.1)"]}, {"extra": "geoip", "requires": ["GeoIP (==1.3.2)", "geoip2 (==2.5.0)", "pycountry (==17.5.14)"]}, {"extra": "dev", "requires": ["Sphinx", "Werkzeug", "django-extensions", "flake8", "mock", "pep8", "tox"]}], "summary": "Django app + middleware to restrict access to all or sections of a Django project by client IP ranges", "version": "1.3.0"}PKR@Jw /django_iprestrict-1.3.0.dist-info/top_level.txtiprestrict PKS@Jndnn'django_iprestrict-1.3.0.dist-info/WHEELWheel-Version: 1.0 Generator: bdist_wheel (0.26.0) Root-Is-Purelib: true Tag: py2-none-any Tag: py3-none-any PKS@Ji:p*django_iprestrict-1.3.0.dist-info/METADATAMetadata-Version: 2.0 Name: django-iprestrict Version: 1.3.0 Summary: Django app + middleware to restrict access to all or sections of a Django project by client IP ranges Home-page: https://github.com/muccg/django-iprestrict Author: Tamas Szabo, CCG, Murdoch University Author-email: devops@ccg.murdoch.edu.au License: UNKNOWN Download-URL: https://github.com/muccg/django-iprestrict/releases Platform: UNKNOWN Classifier: Framework :: Django Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: MIT License Classifier: Operating System :: OS Independent Classifier: Topic :: Software Development Requires-Dist: Django (>=1.8) Requires-Dist: django-templatetag-handlebars (==1.3.1) Provides-Extra: dev Requires-Dist: Sphinx; extra == 'dev' Requires-Dist: Werkzeug; extra == 'dev' Requires-Dist: django-extensions; extra == 'dev' Requires-Dist: flake8; extra == 'dev' Requires-Dist: mock; extra == 'dev' Requires-Dist: pep8; extra == 'dev' Requires-Dist: tox; extra == 'dev' Provides-Extra: geoip Requires-Dist: GeoIP (==1.3.2); extra == 'geoip' Requires-Dist: geoip2 (==2.5.0); extra == 'geoip' Requires-Dist: pycountry (==17.5.14); extra == 'geoip' Django app + middleware to restrict access to all or sections of a Django project by client IP ranges PKS@J+i  (django_iprestrict-1.3.0.dist-info/RECORDdjango_iprestrict-1.3.0.dist-info/DESCRIPTION.rst,sha256=MW1Mpxmn0Ho1mDJ2PJjKS20MmZ8tb4X3lAVDelDxU-Y,104 django_iprestrict-1.3.0.dist-info/METADATA,sha256=piRT56NhWt7tZBsTXyCo5VFRBQNPeb_75DflGM8gzfw,1286 django_iprestrict-1.3.0.dist-info/RECORD,, django_iprestrict-1.3.0.dist-info/WHEEL,sha256=GrqQvamwgBV4nLoJe0vhYRSWzWsx7xjlt74FT0SWYfE,110 django_iprestrict-1.3.0.dist-info/metadata.json,sha256=pEH8v-4nkjTlXVLH1rWh6IjQF6nXic7J5ADO_t8Xg9Q,1099 django_iprestrict-1.3.0.dist-info/top_level.txt,sha256=CeJEtZeoqyJUxGxI__2rkygfrHfNhrmZITWotbsleNA,11 iprestrict/__init__.py,sha256=CxdgGzchttnhcDefpMTg65yBzAipImL2pLd9G9JLe9U,153 iprestrict/admin.py,sha256=jOnDTqu-buFchc7AJa3S348DRj15YXt9hjUl9NFs2Lk,3649 iprestrict/decorators.py,sha256=iej7lbnz-XL7kOVOn5BpsyWklNAXbkFQfPxSwJVQ_ZA,1677 iprestrict/geoip.py,sha256=BiwWI_A-_bP3F7Kl4HL7qfAhUcRuX6h_j41V-ASZkts,2606 iprestrict/ip_utils.py,sha256=VVwm0O6T71FouWTzcen1xpWCzmiskIa-lfl_QeGznt4,2384 iprestrict/middleware.py,sha256=qoTHZ0ADNB8O1PXQTC7A6IdSO6zZIPWMV8G1HW_CUH0,3464 iprestrict/models.py,sha256=r-ggrpBAbc4GwmpQK-K28x5ZGAjDKBMCoEoEkVTazBM,8193 iprestrict/restrictor.py,sha256=0Oq8mwpky3PiI6mVYQ1yRg2BUdt7pLv9iHlSMwXDZtE,668 iprestrict/urls.py,sha256=ZaTfMwxaZC-UoggqlGz-OunkSBEBnYyAWOXgqD-wR9M,585 iprestrict/views.py,sha256=oHFLn9MDi1F2LI1CaHC9_LqEBs5eQ5ys-dsecpL5Jxo,2810 iprestrict/management/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 iprestrict/management/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 iprestrict/management/commands/_utils.py,sha256=-JOQ1EG3rUBLjGHtES0w53dUUK0_0sZ_TSBxHofhH94,384 iprestrict/management/commands/add_ip_to_group.py,sha256=KMx8YKr-3zAjJv7fIBw7g_TdYfBsVI__M5nr5nblm5M,1369 iprestrict/management/commands/import_rules.py,sha256=TCg8aEj7KUyGxhEmSIc8AdoZL0FuKu1lUNQCBnGQZ3M,832 iprestrict/management/commands/importrules.py,sha256=q1aIDDxrFxQJah38V489nPeCo7VOl2qQ-tKsJb31AB0,339 iprestrict/management/commands/reload_rules.py,sha256=UzuhLjJWKYOaS89X5_g5i9vFytJ9-F05vVZdzKztkdc,797 iprestrict/management/commands/reloadrules.py,sha256=4GWgCUpgKtc6dVjxqtaQJQzSpRPfQC_jEXCamTBplJ0,339 iprestrict/migrations/0001_initial.py,sha256=vH3fmyGIbQ_J7US4FOLrpQlrOdyRofxJEDUnVtSa3uU,2184 iprestrict/migrations/0002_initial_data.py,sha256=9nukteVPM5N0_yeK6ejlPnyvpKYCx0mz-o1x7NxtxVc,1614 iprestrict/migrations/0003_add_ipgroup_types.py,sha256=vc4dBulZqkRC_HUhar5ipPA_wx8-OGAhrHmVnIm5eP8,1004 iprestrict/migrations/0004_add_iplocation.py,sha256=agS-56q0jdknhnciKpAFb53tLtp-mPjoVEm3qH8GI8Y,1342 iprestrict/migrations/0005_add_reverse_ipgroup.py,sha256=HxXhR73LoMp6Yj4hhYLFp9wLX8S8LjN6-6CLjvo7Yww,537 iprestrict/migrations/0006_auto_20161013_1327.py,sha256=lInm25e4KU8lCyp6hWQelwyaf0BqN6eTQlvKCQUQ9Xc,972 iprestrict/migrations/__init__.py,sha256=yOrDciBNF3Ei_d4aDoIbLdkLbsavYD3ks2RIwHvS9FU,116 PK=5JuɰAAiprestrict/admin.pyPK>Jvriprestrict/__init__.pyPK=5JJ  ?iprestrict/models.pyPK=5J x r/iprestrict/views.pyPK=5JÜ:iprestrict/restrictor.pyPK=5JJIIo=iprestrict/urls.pyPK=5J??iprestrict/decorators.pyPK=5JN Fiprestrict/middleware.pyPK=5J~P P iTiprestrict/ip_utils.pyPK>J7w;. . ]iprestrict/geoip.pyPK=5JѪO+0Lhiprestrict/migrations/0006_auto_20161013_1327.pyPK=5Jk5{v1fliprestrict/migrations/0005_add_reverse_ipgroup.pyPK=5J#tt!niprestrict/migrations/__init__.pyPK%5HgsNN*oiprestrict/migrations/0002_initial_data.pyPK=5J,5>>,viprestrict/migrations/0004_add_iplocation.pyPK=5J/{iprestrict/migrations/0003_add_ipgroup_types.pyPK%5H=9%iprestrict/migrations/0001_initial.pyPK%5H!iprestrict/management/__init__.pyPK=5J .iprestrict/management/commands/reload_rules.pyPK%5H*Kiprestrict/management/commands/__init__.pyPK=5Jo-tSS-iprestrict/management/commands/importrules.pyPK=5JA'b@@.1iprestrict/management/commands/import_rules.pyPK=5JvSS-iprestrict/management/commands/reloadrules.pyPK=5Jn([iprestrict/management/commands/_utils.pyPK=5JD@YY1!iprestrict/management/commands/add_ip_to_group.pyPKS@J'ܞhh1ɚdjango_iprestrict-1.3.0.dist-info/DESCRIPTION.rstPKS@JwAKK/django_iprestrict-1.3.0.dist-info/metadata.jsonPKR@Jw /django_iprestrict-1.3.0.dist-info/top_level.txtPKS@Jndnn'pdjango_iprestrict-1.3.0.dist-info/WHEELPKS@Ji:p*#django_iprestrict-1.3.0.dist-info/METADATAPKS@J+i  (qdjango_iprestrict-1.3.0.dist-info/RECORDPK ӱ