team-3/src/c3nav/mapdata/models/access.py
2017-10-27 16:40:15 +02:00

101 lines
3.8 KiB
Python

from datetime import timedelta
from django.conf import settings
from django.core.cache import cache
from django.db import models
from django.db.models import Q
from django.utils import timezone
from django.utils.translation import ugettext_lazy as _
from c3nav.mapdata.models import MapUpdate
from c3nav.mapdata.models.base import SerializableMixin, TitledMixin
class AccessRestriction(TitledMixin, models.Model):
"""
An access restriction
"""
users = models.ManyToManyField(settings.AUTH_USER_MODEL, through='AccessPermission')
open = models.BooleanField(default=False, verbose_name=_('open'))
class Meta:
verbose_name = _('Access Restriction')
verbose_name_plural = _('Access Restrictions')
default_related_name = 'accessrestrictions'
@classmethod
def qs_for_request(cls, request):
if request.user.is_authenticated and request.user.is_superuser:
return cls.objects.all()
return cls.objects.none()
class AccessPermission(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
access_restriction = models.ForeignKey(AccessRestriction, on_delete=models.CASCADE)
expire_date = models.DateTimeField(null=True, verbose_name=_('expires'))
class Meta:
verbose_name = _('Access Permission')
verbose_name_plural = _('Access Permissions')
default_related_name = 'accesspermissions'
unique_together = (('user', 'access_restriction'), )
@staticmethod
def user_access_permission_key(user):
return 'mapdata:user_access_permission:%d' % user.pk
@classmethod
def get_for_request(cls, request):
if not request.user.is_authenticated:
return set()
cache_key = cls.user_access_permission_key(request.user)
access_restriction_ids = cache.get(cache_key, None)
if access_restriction_ids is None:
result = tuple(request.user.accesspermissions.filter(
Q(expire_date__isnull=True) | Q(expire_date__lt=timezone.now())
).values_list('access_restriction_id', 'expire_date'))
if result:
access_restriction_ids, expire_dates = zip(*result)
else:
access_restriction_ids, expire_dates = (), ()
expire_date = min((e for e in expire_dates if e), default=timezone.now()+timedelta(seconds=120))
cache.set(cache_key, access_restriction_ids, max(0, (expire_date-timezone.now()).total_seconds()))
return set(access_restriction_ids)
@classmethod
def cache_key_for_request(cls, request):
return '%s:%s' % (
MapUpdate.current_cache_key(),
','.join(str(i) for i in sorted(AccessPermission.get_for_request(request)) or '0')
)
@classmethod
def etag_func(cls, request, *args, **kwargs):
return cls.cache_key_for_request(request)
class AccessRestrictionMixin(SerializableMixin, models.Model):
access_restriction = models.ForeignKey(AccessRestriction, null=True, blank=True,
verbose_name=_('Access Restriction'))
class Meta:
abstract = True
def _serialize(self, **kwargs):
result = super()._serialize(**kwargs)
result['access_restriction'] = self.access_restriction_id
return result
@classmethod
def qs_for_request(cls, request, allow_none=False):
return cls.objects.filter(cls.q_for_request(request, allow_none=allow_none))
@classmethod
def q_for_request(cls, request, prefix='', allow_none=False):
if request is None and allow_none:
return Q()
return (Q(**{prefix+'access_restriction__isnull': True}) |
Q(**{prefix+'access_restriction__in': AccessPermission.get_for_request(request)}))