AccessPermissionToken

This commit is contained in:
Laura Klünder 2017-12-10 03:16:07 +01:00
parent 0c1b27d529
commit cca01e584a
4 changed files with 90 additions and 38 deletions

View file

@ -1,10 +1,6 @@
import time
import uuid
from datetime import timedelta from datetime import timedelta
from itertools import chain from itertools import chain
from django.core.cache import cache
from django.db import transaction
from django.db.models import Q from django.db.models import Q
from django.forms import ChoiceField, Form, ModelForm from django.forms import ChoiceField, Form, ModelForm
from django.utils import timezone from django.utils import timezone
@ -12,7 +8,7 @@ from django.utils.translation import ugettext_lazy as _
from django.utils.translation import ungettext_lazy from django.utils.translation import ungettext_lazy
from c3nav.control.models import UserPermissions from c3nav.control.models import UserPermissions
from c3nav.mapdata.models.access import AccessPermission, AccessRestriction from c3nav.mapdata.models.access import AccessPermissionToken, AccessRestriction
class UserPermissionsForm(ModelForm): class UserPermissionsForm(ModelForm):
@ -100,21 +96,7 @@ class AccessPermissionForm(Form):
def save(self, user): def save(self, user):
self._save_code(self._create_code(), user) self._save_code(self._create_code(), user)
def create_code(self, timeout=30): def get_token(self):
code = uuid.uuid4()
cache.set('access:code:%s' % code, (self._create_code(), time.time()+timeout), timeout)
def save_code(self, code, user):
cache_key = 'access:code:%s' % code
with transaction.atomic():
AccessPermission.objects.select_for_update().first()
code, expires = cache.get(cache_key, (None, None))
if code is None or expires < time.time():
raise ValueError
self._save_code(code, user)
cache.delete(cache_key)
def _create_code(self):
restrictions = [] restrictions = []
for restriction in self.cleaned_data['access_restrictions']: for restriction in self.cleaned_data['access_restrictions']:
expires = self.cleaned_data['expires'] expires = self.cleaned_data['expires']
@ -122,19 +104,6 @@ class AccessPermissionForm(Form):
if author_expires is not None: if author_expires is not None:
expires = author_expires if expires is None else min(expires, author_expires) expires = author_expires if expires is None else min(expires, author_expires)
restrictions.append((restriction.pk, expires)) restrictions.append((restriction.pk, expires))
return (tuple(restrictions), self.author.pk, self.cleaned_data.get('can_grant', '0') == '1') return AccessPermissionToken(author=self.author,
can_grant=self.cleaned_data.get('can_grant', '0') == '1',
@classmethod restrictions=tuple(restrictions))
def _save_code(cls, code, user):
restrictions, author_id, can_grant = code
print(code)
with transaction.atomic():
for pk, expire_date in restrictions:
obj, created = AccessPermission.objects.get_or_create(
user=user,
access_restriction_id=pk
)
obj.author_id = author_id
obj.expire_date = expire_date
obj.can_grant = can_grant
obj.save()

View file

@ -101,8 +101,8 @@ def user_detail(request, user):
if request.method == 'POST' and request.POST.get('submit_access_permissions'): if request.method == 'POST' and request.POST.get('submit_access_permissions'):
form = AccessPermissionForm(request=request, data=request.POST) form = AccessPermissionForm(request=request, data=request.POST)
if form.is_valid(): if form.is_valid():
form.save(user) form.get_token().redeem(user)
messages.success(request, _('Access permissions successfully updated.')) messages.success(request, _('Access permissions successfully granted.'))
return redirect(request.path_info) return redirect(request.path_info)
else: else:
form = AccessPermissionForm(request=request) form = AccessPermissionForm(request=request)

View file

@ -0,0 +1,33 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.7 on 2017-12-10 02:10
from __future__ import unicode_literals
import c3nav.mapdata.models.access
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import uuid
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('mapdata', '0055_grant_access_permissions'),
]
operations = [
migrations.CreateModel(
name='AccessPermissionToken',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('valid_until', models.DateTimeField(db_index=True, default=c3nav.mapdata.models.access.default_valid_until, verbose_name='valid until')),
('unlimited', models.BooleanField(db_index=True, default=False, verbose_name='unlimited')),
('redeemed', models.BooleanField(db_index=True, default=False, verbose_name='redeemed')),
('can_grant', models.BooleanField(db_index=True, default=False, verbose_name='can grant')),
('data', models.BinaryField()),
('author', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='created_accesspermission_tokens', to=settings.AUTH_USER_MODEL, verbose_name='author')),
('redeemed_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='redeemed_accesspermission_tokens', to=settings.AUTH_USER_MODEL, verbose_name='redeemed by')),
],
),
]

View file

@ -1,3 +1,5 @@
import pickle
import uuid
from datetime import timedelta from datetime import timedelta
from django.conf import settings from django.conf import settings
@ -29,6 +31,54 @@ class AccessRestriction(TitledMixin, models.Model):
return cls.objects.all() return cls.objects.all()
def default_valid_until():
return timezone.now()+timedelta(seconds=20)
class AccessPermissionToken(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.PROTECT,
related_name='created_accesspermission_tokens',
verbose_name=_('author'))
valid_until = models.DateTimeField(db_index=True, default=default_valid_until,
verbose_name=_('valid until'))
unlimited = models.BooleanField(default=False, db_index=True, verbose_name=_('unlimited'))
redeemed = models.BooleanField(default=False, db_index=True, verbose_name=_('redeemed'))
redeemed_by = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, on_delete=models.SET_NULL,
related_name='redeemed_accesspermission_tokens',
verbose_name=_('redeemed by'))
can_grant = models.BooleanField(default=False, db_index=True, verbose_name=_('can grant'))
data = models.BinaryField()
@property
def restrictions(self):
return pickle.loads(self.data)
@restrictions.setter
def restrictions(self, value):
self.data = pickle.dumps(value)
def redeem(self, user=None):
if self.redeemed_by_id or (user is None and self.redeemed):
raise TypeError('Already redeemed.')
self.redeemed = True
if user:
for pk, expire_date in self.restrictions:
obj, created = AccessPermission.objects.get_or_create(
user=user,
access_restriction_id=pk
)
obj.author_id = self.author_id
obj.expire_date = expire_date
obj.can_grant = self.can_grant
obj.save()
self.redeemed_by = user
if self.pk:
self.save()
class AccessPermission(models.Model): class AccessPermission(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
access_restriction = models.ForeignKey(AccessRestriction, on_delete=models.CASCADE) access_restriction = models.ForeignKey(AccessRestriction, on_delete=models.CASCADE)