AccessPermissionToken
This commit is contained in:
parent
0c1b27d529
commit
cca01e584a
4 changed files with 90 additions and 38 deletions
|
@ -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()
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
33
src/c3nav/mapdata/migrations/0056_accesspermissiontoken.py
Normal file
33
src/c3nav/mapdata/migrations/0056_accesspermissiontoken.py
Normal 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')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
|
@ -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)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue