2017-10-24 22:52:38 +02:00
|
|
|
from datetime import timedelta
|
|
|
|
|
2017-10-24 22:13:53 +02:00
|
|
|
from django.conf import settings
|
2017-10-24 22:45:57 +02:00
|
|
|
from django.core.cache import cache
|
2017-12-08 22:26:14 +01:00
|
|
|
from django.db import models, transaction
|
2017-07-13 19:22:57 +02:00
|
|
|
from django.db.models import Q
|
2017-10-24 22:45:57 +02:00
|
|
|
from django.utils import timezone
|
2017-07-13 18:43:03 +02:00
|
|
|
from django.utils.translation import ugettext_lazy as _
|
|
|
|
|
2017-10-27 16:40:15 +02:00
|
|
|
from c3nav.mapdata.models import MapUpdate
|
2017-07-13 18:43:03 +02:00
|
|
|
from c3nav.mapdata.models.base import SerializableMixin, TitledMixin
|
|
|
|
|
|
|
|
|
|
|
|
class AccessRestriction(TitledMixin, models.Model):
|
|
|
|
"""
|
2017-10-24 22:13:53 +02:00
|
|
|
An access restriction
|
2017-07-13 18:43:03 +02:00
|
|
|
"""
|
2017-12-08 21:31:53 +01:00
|
|
|
users = models.ManyToManyField(settings.AUTH_USER_MODEL, through='AccessPermission',
|
|
|
|
through_fields=('access_restriction', 'user'))
|
2017-10-24 22:13:53 +02:00
|
|
|
open = models.BooleanField(default=False, verbose_name=_('open'))
|
2017-07-13 18:43:03 +02:00
|
|
|
|
|
|
|
class Meta:
|
|
|
|
verbose_name = _('Access Restriction')
|
|
|
|
verbose_name_plural = _('Access Restrictions')
|
|
|
|
default_related_name = 'accessrestrictions'
|
|
|
|
|
2017-07-13 18:54:49 +02:00
|
|
|
@classmethod
|
|
|
|
def qs_for_request(cls, request):
|
2017-12-08 22:32:35 +01:00
|
|
|
return cls.objects.all()
|
2017-07-13 18:54:49 +02:00
|
|
|
|
2017-07-13 18:43:03 +02:00
|
|
|
|
2017-10-24 22:13:53 +02:00
|
|
|
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'))
|
2017-12-08 21:31:53 +01:00
|
|
|
can_grant = models.BooleanField(default=False, verbose_name=_('can grant'))
|
|
|
|
author = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, on_delete=models.SET_NULL,
|
|
|
|
related_name='authored_access_permissions', verbose_name=_('Author'))
|
2017-10-24 22:13:53 +02:00
|
|
|
|
|
|
|
class Meta:
|
|
|
|
verbose_name = _('Access Permission')
|
|
|
|
verbose_name_plural = _('Access Permissions')
|
|
|
|
default_related_name = 'accesspermissions'
|
|
|
|
unique_together = (('user', 'access_restriction'), )
|
|
|
|
|
2017-10-24 22:45:57 +02:00
|
|
|
@staticmethod
|
2017-12-08 22:26:14 +01:00
|
|
|
def user_access_permission_key(user_id):
|
|
|
|
return 'mapdata:user_access_permission:%d' % user_id
|
2017-10-24 22:45:57 +02:00
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def get_for_request(cls, request):
|
|
|
|
if not request.user.is_authenticated:
|
|
|
|
return set()
|
|
|
|
|
2017-12-08 22:26:14 +01:00
|
|
|
cache_key = cls.user_access_permission_key(request.user.pk)
|
2017-10-24 22:45:57 +02:00
|
|
|
access_restriction_ids = cache.get(cache_key, None)
|
|
|
|
if access_restriction_ids is None:
|
2017-10-24 22:52:38 +02:00
|
|
|
result = tuple(request.user.accesspermissions.filter(
|
2017-10-24 22:45:57 +02:00
|
|
|
Q(expire_date__isnull=True) | Q(expire_date__lt=timezone.now())
|
2017-10-24 22:52:38 +02:00
|
|
|
).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()))
|
2017-10-24 23:24:45 +02:00
|
|
|
return set(access_restriction_ids)
|
2017-10-24 22:45:57 +02:00
|
|
|
|
2017-10-27 16:40:15 +02:00
|
|
|
@classmethod
|
2017-10-27 17:08:36 +02:00
|
|
|
def cache_key_for_request(cls, request, with_update=True):
|
|
|
|
return (
|
|
|
|
((MapUpdate.current_cache_key()+':') if with_update else '') +
|
2017-10-27 16:40:15 +02:00
|
|
|
','.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)
|
|
|
|
|
2017-12-08 22:26:14 +01:00
|
|
|
def save(self, *args, **kwargs):
|
|
|
|
with transaction.atomic():
|
|
|
|
super().save(*args, **kwargs)
|
|
|
|
transaction.on_commit(lambda: cache.delete(self.user_access_permission_key(self.user_id)))
|
|
|
|
|
|
|
|
def delete(self, *args, **kwargs):
|
|
|
|
with transaction.atomic():
|
|
|
|
super().delete(*args, **kwargs)
|
|
|
|
transaction.on_commit(lambda: cache.delete(self.user_access_permission_key(self.user_id)))
|
|
|
|
|
2017-10-24 22:13:53 +02:00
|
|
|
|
2017-07-13 18:43:03 +02:00
|
|
|
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
|
2017-07-13 18:54:49 +02:00
|
|
|
|
2017-11-02 13:35:58 +01:00
|
|
|
def details_display(self):
|
|
|
|
result = super().details_display()
|
|
|
|
result['display'].extend([
|
2017-11-30 01:10:49 +01:00
|
|
|
(_('Access Restriction'), self.access_restriction_id and self.access_restriction.title),
|
2017-11-02 13:35:58 +01:00
|
|
|
])
|
|
|
|
return result
|
|
|
|
|
2017-07-13 18:54:49 +02:00
|
|
|
@classmethod
|
2017-08-06 22:07:34 +02:00
|
|
|
def qs_for_request(cls, request, allow_none=False):
|
|
|
|
return cls.objects.filter(cls.q_for_request(request, allow_none=allow_none))
|
2017-07-13 22:22:13 +02:00
|
|
|
|
|
|
|
@classmethod
|
2017-08-06 22:07:34 +02:00
|
|
|
def q_for_request(cls, request, prefix='', allow_none=False):
|
2017-12-08 22:32:35 +01:00
|
|
|
if request is None and allow_none:
|
2017-07-13 22:22:13 +02:00
|
|
|
return Q()
|
2017-10-24 22:45:57 +02:00
|
|
|
return (Q(**{prefix+'access_restriction__isnull': True}) |
|
2017-11-25 13:47:47 +01:00
|
|
|
Q(**{prefix+'access_restriction__pk__in': AccessPermission.get_for_request(request)}))
|