Skip to content
This repository has been archived by the owner on Mar 2, 2019. It is now read-only.

Commit

Permalink
security: made all internet switches require POST to prevent CSRF; ad…
Browse files Browse the repository at this point in the history
…ded new permissions can_reset_own_quota, can_revoke_access, can_toggle_internet; added new limits to prevent a user from resetting another users quota beyond a defined number of times and from signing in a user with more than a defined amount of quota
  • Loading branch information
micolous committed Jun 5, 2012
1 parent 3743f84 commit 8327aac
Show file tree
Hide file tree
Showing 7 changed files with 375 additions and 123 deletions.
@@ -0,0 +1,156 @@
# -*- coding: utf-8 -*-
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models


class Migration(SchemaMigration):

def forwards(self, orm):
# Adding field 'UserProfile.maximum_quota_signins'
db.add_column('frontend_userprofile', 'maximum_quota_signins',
self.gf('django.db.models.fields.PositiveIntegerField')(default=0),
keep_default=False)

# Adding field 'UserProfile.maximum_quota_resets'
db.add_column('frontend_userprofile', 'maximum_quota_resets',
self.gf('django.db.models.fields.PositiveIntegerField')(default=0),
keep_default=False)

def backwards(self, orm):
# Deleting field 'UserProfile.maximum_quota_signins'
db.delete_column('frontend_userprofile', 'maximum_quota_signins')

# Deleting field 'UserProfile.maximum_quota_resets'
db.delete_column('frontend_userprofile', 'maximum_quota_resets')

models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
'auth.permission': {
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'frontend.event': {
'Meta': {'ordering': "['start']", 'object_name': 'Event'},
'end': ('django.db.models.fields.DateTimeField', [], {}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '50'}),
'start': ('django.db.models.fields.DateTimeField', [], {})
},
'frontend.eventattendance': {
'Meta': {'ordering': "['event', 'user_profile']", 'object_name': 'EventAttendance'},
'coffee': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'event': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['frontend.Event']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'quota_amount': ('django.db.models.fields.BigIntegerField', [], {'default': '157286400L'}),
'quota_multiplier': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}),
'quota_unmetered': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'quota_used': ('django.db.models.fields.BigIntegerField', [], {'default': '0'}),
'registered_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'registered_by'", 'null': 'True', 'to': "orm['frontend.UserProfile']"}),
'registered_on': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'user_profile': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['frontend.UserProfile']"})
},
'frontend.ip4portforward': {
'Meta': {'object_name': 'IP4PortForward'},
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'creator': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['frontend.UserProfile']"}),
'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'external_port': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
'host': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['frontend.NetworkHost']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'label': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '64', 'blank': 'True'}),
'port': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
'protocol': ('django.db.models.fields.related.ForeignKey', [], {'default': '6', 'to': "orm['frontend.IP4Protocol']"})
},
'frontend.ip4protocol': {
'Meta': {'ordering': "['name']", 'object_name': 'IP4Protocol'},
'description': ('django.db.models.fields.CharField', [], {'max_length': '128', 'blank': 'True'}),
'has_port': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '16'})
},
'frontend.networkhost': {
'Meta': {'ordering': "['mac_address']", 'object_name': 'NetworkHost'},
'computer_name': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'first_connection': ('django.db.models.fields.DateTimeField', [], {}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'ip_address': ('django.db.models.fields.IPAddressField', [], {'max_length': '15'}),
'mac_address': ('django.db.models.fields.CharField', [], {'max_length': '12'}),
'online': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'user_profile': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['frontend.UserProfile']", 'null': 'True', 'blank': 'True'})
},
'frontend.networkhostownerchangeevent': {
'Meta': {'ordering': "['when']", 'object_name': 'NetworkHostOwnerChangeEvent'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'network_host': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['frontend.NetworkHost']"}),
'new_owner': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'new_owner'", 'null': 'True', 'to': "orm['frontend.UserProfile']"}),
'old_owner': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'old_owner'", 'null': 'True', 'to': "orm['frontend.UserProfile']"}),
'when': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'})
},
'frontend.networkusagedatapoint': {
'Meta': {'ordering': "['event_attendance', 'when']", 'object_name': 'NetworkUsageDataPoint'},
'bytes': ('django.db.models.fields.BigIntegerField', [], {}),
'event_attendance': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['frontend.EventAttendance']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'when': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'})
},
'frontend.oui': {
'Meta': {'ordering': "['hex']", 'object_name': 'Oui'},
'full_name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'hex': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '6'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_console': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50'})
},
'frontend.quotaresetevent': {
'Meta': {'ordering': "['when']", 'object_name': 'QuotaResetEvent'},
'event_attendance': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['frontend.EventAttendance']"}),
'excuse': ('django.db.models.fields.CharField', [], {'max_length': '256'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'performer': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'performer'", 'to': "orm['frontend.UserProfile']"}),
'when': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'})
},
'frontend.userprofile': {
'Meta': {'ordering': "['user__username']", 'object_name': 'UserProfile'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'internet_on': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'maximum_quota_resets': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
'maximum_quota_signins': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
'theme': ('django.db.models.fields.CharField', [], {'default': "'cake'", 'max_length': '30'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'user'", 'unique': 'True', 'to': "orm['auth.User']"})
}
}

complete_apps = ['frontend']
28 changes: 28 additions & 0 deletions tollgate/frontend/models.py
Expand Up @@ -79,6 +79,32 @@ class Meta:
internet_on = BooleanField(default=True)
theme = CharField(default='cake', max_length=30, choices=THEME_CHOICES)

maximum_quota_signins = PositiveIntegerField(
default=0,
help_text=_("""\
Sets the maximum amount of quota in MiB that the user may grant to
users they sign in. If set to 0, they will be able to set any quota
amount. Setting this value disallows setting unlimited quota.
This will only have effect if the user has been granted permission
to sign in users. Otherwise, they will not be able to sign in users.
""")
)

maximum_quota_resets = PositiveIntegerField(
default=0,
help_text=_("""\
Sets the maximum amount of quota resets that the user may perform on
other users. If set to 0, they will be able to reset quota an
unlimited number of times. If set to 1, this will mean that the user
can only perform the "one free reset" on behalf of another user.
This will only have effect if the user has been granted permission
to reset user quota for other users. Otherwise, they will not be able
to reset quota for other users at all.
""")
)

def get_hosts(self):
return NetworkHost.objects.filter(user_profile=self)

Expand Down Expand Up @@ -179,6 +205,8 @@ class Meta:
("can_view_quota", "View quota"),
("can_reset_quota", "Reset quota"),
("can_change_coffee", "Coffee request access change"), # this is a seperate ACL because of ravenge and dasman
('can_reset_own_quota', 'Reset own quota multiple times'),
('can_revoke_access', 'Revoke internet access for a user'),
)
event = ForeignKey(Event)
user_profile = ForeignKey(UserProfile)
Expand Down
12 changes: 10 additions & 2 deletions tollgate/frontend/templates/frontend/quota.html
Expand Up @@ -170,9 +170,17 @@ <h2>Internet Switch</h2>
{% endif %}
<ul>
{% if not attendance.is_revoked %}
<li><a href="{% url 'quota-on' %}">Switch on internet connectivity.</a> If you have quota available, internet access will be granted to all of your online computers and devices.</li>
<li>
<form method="post" action="{% url 'quota-on' %}">{% csrf_token %}
<input type="submit" value="Switch on internet connectivity" /> If you have quota available, internet access will be granted to all of your online computers and devices.
</form>
</li>
{% endif %}
<li><a href="{% url 'quota-off' %}">Switch off internet connectivity.</a> All computers listed here will lose internet access <em>immediately</em>, and all unfinished downloads will be terminated. Internet access will resume when you opt to, or when you try to connect another computer to the internet in your name.</li>
<li>
<form method="post" action="{% url 'quota-off' %}">{% csrf_token %}
<input type="submit" value="Switch off internet connectivity" /> All computers listed here will lose internet access <em>immediately</em>, and all unfinished downloads will be terminated. Internet access will resume when you opt to, or when you try to connect another computer to the internet in your name.
</form>
</li>
</ul>

<h2>Your Computers and Devices</h2>
Expand Down
28 changes: 22 additions & 6 deletions tollgate/frontend/templates/frontend/usage-info.html
Expand Up @@ -19,8 +19,8 @@
{% load url from future %}
{% load i18n %}
{% load humanize %}
{% block windowtitle %}internet usage report for {{ a.user_profile.user.username }} {% endblock %}
{% block title %}internet usage report for {{ a.user_profile.user.username }} {% endblock %}
{% block windowtitle %}internet usage report for {{ a.user_profile.user.username }} at {{ a.event.name }}{% endblock %}
{% block title %}internet usage report for {{ a.user_profile.user.username }} at {{ a.event.name }}{% endblock %}

{% block content %}
{% if quota_update_fail %}
Expand All @@ -46,9 +46,17 @@ <h2>Internet Switch (currently {% if a.user_profile.internet_on %}<span class="y
<ul>
{% if perms.frontend.can_toggle_internet %}
{% if not a.is_revoked %}
<li><a href="{% url 'usage-on' a.id %}">Switch on {{ a.user_profile.user.username }}'s internet connectivity.</a> If they have quota available, internet access will be granted to all of their computers and devices.</li>
<li>
<form method="post" action="{% url 'usage-on' a.id %}">{% csrf_token %}
<input type="submit" value="Switch on {{ a.user_profile.user.username }}'s internet connectivity" /> If they have quota available, internet access will be granted to all of their computers and devices.
</form>
</li>
{% endif %}
<li><a href="{% url 'usage-off' a.id %}">Switch off {{ a.user_profile.user.username }}'s internet connectivity.</a> All computers listed here will loose internet access <em>immediately</em>, and all unfinished downloads will be terminated. Internet access will resume when you or they opt to, or when they try to connect another computer to the internet in their name.</li>
<li>
<form method="post" action="{% url 'usage-off' a.id %}">{% csrf_token %}
<input type="submit" value="Switch off {{ a.user_profile.user.username }}'s internet connectivity" /> All computers listed here will loose internet access <em>immediately</em>, and all unfinished downloads will be terminated. Internet access will resume when you or they opt to, or when they try to connect another computer to the internet in their name.
</form>
</li>
{% else %}
<li>You're not authorised to toggle other users' internet access, you need <code>can_toggle_internet</code>.</li>
{% endif %}
Expand All @@ -65,9 +73,17 @@ <h2>Internet Switch (currently {% if a.user_profile.internet_on %}<span class="y
</form>
</li>
{% endif %}
<li><a href="{% url 'usage-disable' a.id %}">Revoke internet access rights.</a> The user will loose their unlimited status (if any), and their available quota multiplier will be set to zero. Internet access will be terminated, and can only be reinstated by an administrator.</li>
{% else %}
<li>You're not authorised to reset user quotas or revoke internet access, you need <code>can_reset_quota</code>.</li>
<li>You're not authorised to reset user quotas, you need <code>can_reset_quota</code>.</li>
{% endif %}
{% if perms.frontend.can_revoke_access %}
<li>
<form method="post" action="{% url 'usage-disable' a.id %}">{% csrf_token %}
<input type="submit" value="Revoke internet access rights" /> The user will loose their unlimited status (if any), and their available quota multiplier will be set to zero. Internet access will be terminated, and can only be reinstated by an administrator.
</form>
</li>
{% else %}
<li>You're not authorised to revoke internet access, you need <code>can_revoke_access</code>.</li>
{% endif %}
</ul>

Expand Down
18 changes: 15 additions & 3 deletions tollgate/frontend/templates/frontend/usage.html
Expand Up @@ -30,9 +30,21 @@

<ul>
{% if perms.frontend.can_toggle_internet %}
<li><a href="{% url 'usage-all-on' %}">Turn on everyone's internet access that has opted to switch it on</a></li>
<li><a href="{% url 'usage-all-really-on' %}">Turn on everyone's internet access, even if they have disabled it</a></li>
<li><a href="{% url 'usage-all-off' %}">Turn off everyone's internet access.</a></li>
<li>
<form method="post" action="{% url 'usage-all-on' %}">{% csrf_token %}
<input type="submit" value="Turn on everyone's internet access that has opted to switch it on" />
</form>
</li>
<li>
<form method="post" action="{% url 'usage-all-really-on' %}">{% csrf_token %}
<input type="submit" value="Turn on everyone's internet access, even if they have disabled it" />
</form>
</li>
<li>
<form method="post" action="{% url 'usage-all-off' %}">{% csrf_token %}
<input type="submit" value="Turn off everyone's internet access" />
</form>
</li>
{% else %}
<li>You're not authorised to toggle other users' internet access, you need <code>can_toggle_internet</code>.</li>
{% endif %}
Expand Down
1 change: 0 additions & 1 deletion tollgate/frontend/urls.py
Expand Up @@ -67,7 +67,6 @@
url(r'^usage/(?P<aid>\d+)/off/$', 'usage_off', name='usage-off'),
url(r'^usage/(?P<aid>\d+)/reset/$', 'usage_reset', name='usage-reset'),
url(r'^usage/(?P<aid>\d+)/disable/$', 'usage_disable', name='usage-disable'),
url(r'^usage/(?P<aid>\d+)/coffee/$', 'usage_coffee', name='usage-coffee'),
#url(r'^usage/(?P<aid>\d+)/graph.png$', 'usage_graph', name='usage-graph'),
url(r'^usage/(?P<aid>\d+)/$', 'usage_info', name='usage-info'),
url(r'^usage/$', 'usage', name='usage'),
Expand Down

0 comments on commit 8327aac

Please sign in to comment.