diff --git a/src/c3nav/access/__init__.py b/src/c3nav/access/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/src/c3nav/access/admin.py b/src/c3nav/access/admin.py deleted file mode 100644 index 48d993be..00000000 --- a/src/c3nav/access/admin.py +++ /dev/null @@ -1,66 +0,0 @@ -from django.contrib import admin -from django.contrib.auth.admin import UserAdmin as BaseUserAdmin -from django.contrib.auth.models import User -from django.utils.translation import ugettext_lazy as _ - -from c3nav.access.models import AccessOperator, AccessToken, AccessTokenInstance, AccessUser - - -class AccessOperatorInline(admin.StackedInline): - model = AccessOperator - can_delete = False - - -class UserAdmin(BaseUserAdmin): - fieldsets = ( - (None, {'fields': ('username', 'password', 'email')}), - (_('Permissions'), {'fields': ('is_active', 'is_staff', 'is_superuser', - 'groups', 'user_permissions')}), - (_('Important dates'), {'fields': ('last_login', 'date_joined')}), - ) - readonly_fields = ('last_login', 'date_joined',) - inlines = (AccessOperatorInline, ) - - -admin.site.unregister(User) -admin.site.register(User, UserAdmin) - - -class AccessTokenInline(admin.TabularInline): - model = AccessToken - show_change_link = True - readonly_fields = ('author', 'permissions', 'description', 'creation_date', 'activated', 'expires', 'expired') - - def has_add_permission(self, request): - return False - - -@admin.register(AccessUser) -class AccessUserAdmin(admin.ModelAdmin): - inlines = (AccessTokenInline,) - list_display = ('user_url', 'creation_date', 'author', 'description') - fields = ('user_url', 'creation_date', 'author', 'description') - readonly_fields = ('creation_date', ) - - -class AccessTokenInstanceInline(admin.TabularInline): - model = AccessTokenInstance - fields = ('secret', 'creation_date', 'expires', ) - readonly_fields = ('secret', 'creation_date', 'expires', ) - - def has_add_permission(self, request): - return False - - def has_change_permission(self, request, obj=None): - return False - - -@admin.register(AccessToken) -class AccessTokenAdmin(admin.ModelAdmin): - inlines = (AccessTokenInstanceInline,) - list_display = ('__str__', 'user', 'permissions', 'author', 'creation_date', 'activated', 'expires', 'expired') - fields = ('user', 'permissions', 'author', 'creation_date', 'activated', 'expires', 'expired', 'description') - readonly_fields = ('user', 'creation_date', 'activated', 'expired', 'description') - - def has_add_permission(self, request): - return False diff --git a/src/c3nav/access/apply.py b/src/c3nav/access/apply.py deleted file mode 100644 index 7db0bb87..00000000 --- a/src/c3nav/access/apply.py +++ /dev/null @@ -1,27 +0,0 @@ -from django.db.models import Q - -from c3nav.mapdata.inclusion import get_maybe_invisible_areas_names - - -def can_access(request, item): - # todo implement this - return True - - -def filter_queryset_by_access(request, queryset, filter_location_inclusion=False): - # todo implement this - return queryset if request.c3nav_full_access else queryset.filter(public=True) - - -def filter_arealocations_by_access(request, queryset): - # todo implement this - if request.c3nav_full_access: - return queryset - return queryset.filter(Q(Q(public=True), ~Q(routing_inclusion='needs_permission')) | - Q(name__in=request.c3nav_access_list)) - - -def get_visible_areas(request): - areas = [':full' if request.c3nav_full_access else ':base'] - areas += [name for name in get_maybe_invisible_areas_names() if name in request.c3nav_access_list] - return areas diff --git a/src/c3nav/access/forms.py b/src/c3nav/access/forms.py deleted file mode 100644 index cfbba04a..00000000 --- a/src/c3nav/access/forms.py +++ /dev/null @@ -1,76 +0,0 @@ -from django.forms import ModelForm, MultipleChoiceField -from django.utils.translation import ugettext_lazy as _ - -from c3nav.access.models import AccessToken, AccessUser - - -def get_permissions_field(request): - locations = AreaLocation.objects.filter(routing_inclusion='needs_permission').order_by('name') - - has_operator = True - try: - request.user.operator - except: - has_operator = False - - OPTIONS = [] - can_full = False - if request.user.is_superuser: - can_full = True - elif has_operator: - can_award = request.user.operator.can_award_permissions.split(';') - can_full = ':full' in can_award - if not can_full: - locations = locations.filter(name__in=can_award) - else: - locations = [] - - if can_full: - OPTIONS.append((':full', _('Full Permissions'))) - - locationgroups = {} - locationgroups_count = {} - for location in locations: - for group in location.groups.all(): - if group.location_id not in locationgroups: - locationgroups[group.location_id] = group - locationgroups_count[group.location_id] = 0 - locationgroups_count[group.location_id] += 1 - - locationgroup_options = [] - for location_id, group in locationgroups.items(): - locationgroup_options.append((location_id, _('%(grouptitle)s (%(count)s nonpublic locations)') % { - 'grouptitle': group.title, - 'count': locationgroups_count[location_id] - })) - - OPTIONS += sorted(locationgroup_options) - - if can_full: - OPTIONS.append((':full', _('Full Permissions'))) - - OPTIONS += [(location.name, location.title) for location in locations] - return MultipleChoiceField(choices=OPTIONS, required=True) - - -class AccessTokenForm(ModelForm): - def __init__(self, *args, request, **kwargs): - super().__init__(*args, **kwargs) - self.fields['permissions'] = get_permissions_field(request) - - class Meta: - model = AccessToken - fields = ['permissions', 'description', 'expires'] - - def clean_permissions(self): - data = self.cleaned_data['permissions'] - if ':full' in data: - data = [':full'] - data = ';'.join(data) - return data - - -class AccessUserForm(ModelForm): - class Meta: - model = AccessUser - fields = ['user_url', 'description'] diff --git a/src/c3nav/access/middleware.py b/src/c3nav/access/middleware.py deleted file mode 100644 index 81b09440..00000000 --- a/src/c3nav/access/middleware.py +++ /dev/null @@ -1,57 +0,0 @@ -import re -from datetime import timedelta - -from django.conf import settings -from django.db import transaction -from django.db.models import Q -from django.utils import timezone - -from c3nav.access.models import AccessTokenInstance - - -class AccessTokenMiddleware: - def __init__(self, get_response): - self.get_response = get_response - - def __call__(self, request): - request.c3nav_access_instance = None - request.c3nav_access = None - request.c3nav_new_access = False - - request.c3nav_full_access = settings.DEBUG - request.c3nav_access_list = () - - access_cookie = request.COOKIES.get('c3nav_access') - if access_cookie and re.match(r'^[0-9]+:[a-zA-Z0-9]+$', access_cookie): - pk, secret = access_cookie.split(':') - queryset = AccessTokenInstance.objects.filter(Q(access_token__id=int(pk), secret=secret), - Q(expires__isnull=True) | Q(expires__gt=timezone.now()), - Q(access_token__expired=False), - Q(access_token__expires__isnull=True) | - Q(access_token__expires__gt=timezone.now())) - access_instance = queryset.select_related('access_token').first() - if access_instance: - request.c3nav_access_instance = access_instance - request.c3nav_access = access_instance.access_token - request.c3nav_access.instances.filter(creation_date__lt=access_instance.creation_date).delete() - - request.c3nav_full_access = request.c3nav_access.full_access - request.c3nav_access_list = request.c3nav_access.permissions_list - - response = self.get_response(request) - - if request.c3nav_access is not None: - with transaction.atomic(): - cookie_value = request.c3nav_access.new_instance() - response.set_cookie('c3nav_access', cookie_value, expires=timezone.now() + timedelta(days=30)) - - if request.c3nav_new_access: - request.c3nav_access.activated = True - request.c3nav_access.save() - - if request.c3nav_access_instance: - access_token = request.c3nav_access_instance.access_token - access_token.expired = True - access_token.save() - - return response diff --git a/src/c3nav/access/migrations/0001_initial.py b/src/c3nav/access/migrations/0001_initial.py deleted file mode 100644 index 13d446ea..00000000 --- a/src/c3nav/access/migrations/0001_initial.py +++ /dev/null @@ -1,83 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.10.4 on 2016-12-21 17:21 -from __future__ import unicode_literals - -from django.conf import settings -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - initial = True - - dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ] - - operations = [ - migrations.CreateModel( - name='AccessOperator', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('description', models.TextField(blank=True, null=True, verbose_name='description')), - ('can_award_permissions', models.CharField(max_length=2048, verbose_name='can award permissions')), - ('access_from', models.DateTimeField(blank=True, null=True, verbose_name='has access from')), - ('access_until', models.DateTimeField(blank=True, null=True, verbose_name='has access until')), - ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='operator', to=settings.AUTH_USER_MODEL)), - ], - options={ - 'verbose_name_plural': 'Access Operator', - 'verbose_name': 'Access Operator', - }, - ), - migrations.CreateModel( - name='AccessToken', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('permissions', models.CharField(max_length=2048, verbose_name='permissions')), - ('description', models.CharField(max_length=200, verbose_name='description')), - ('creation_date', models.DateTimeField(auto_now_add=True, verbose_name='creation date')), - ('expires', models.DateTimeField(blank=True, null=True)), - ('expired', models.BooleanField(default=False, verbose_name='is expired')), - ('author', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL, verbose_name='creator')), - ], - options={ - 'verbose_name_plural': 'Access Tokens', - 'verbose_name': 'Access Token', - }, - ), - migrations.CreateModel( - name='AccessTokenInstance', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('secret', models.CharField(max_length=42, verbose_name='access secret')), - ('creation_date', models.DateTimeField(auto_now_add=True, verbose_name='creation date')), - ('expires', models.DateTimeField(null=True)), - ('access_token', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='instances', to='access.AccessToken', verbose_name='Access Token')), - ], - options={ - 'verbose_name_plural': 'Access Tokens Instance', - 'verbose_name': 'Access Token Instance', - }, - ), - migrations.CreateModel( - name='AccessUser', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('user_url', models.CharField(help_text='Usually an URL to a profile somewhere', max_length=200, unique=True, verbose_name='access name')), - ('description', models.TextField(blank=True, max_length=200, null=True, verbose_name='description')), - ('creation_date', models.DateTimeField(auto_now_add=True, verbose_name='creation date')), - ('author', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='access.AccessOperator', verbose_name='creator')), - ], - options={ - 'verbose_name_plural': 'Access Users', - 'verbose_name': 'Access User', - }, - ), - migrations.AddField( - model_name='accesstoken', - name='user', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='tokens', to='access.AccessUser', verbose_name='Access User'), - ), - ] diff --git a/src/c3nav/access/migrations/0002_auto_20161221_1739.py b/src/c3nav/access/migrations/0002_auto_20161221_1739.py deleted file mode 100644 index 49f42e84..00000000 --- a/src/c3nav/access/migrations/0002_auto_20161221_1739.py +++ /dev/null @@ -1,26 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.10.4 on 2016-12-21 17:39 -from __future__ import unicode_literals - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('access', '0001_initial'), - ] - - operations = [ - migrations.AddField( - model_name='accesstoken', - name='activated', - field=models.BooleanField(default=False, verbose_name='activated'), - ), - migrations.AddField( - model_name='accesstoken', - name='secret', - field=models.CharField(default='', max_length=42, verbose_name='activation secret'), - preserve_default=False, - ), - ] diff --git a/src/c3nav/access/migrations/0003_auto_20161221_2311.py b/src/c3nav/access/migrations/0003_auto_20161221_2311.py deleted file mode 100644 index 7815884d..00000000 --- a/src/c3nav/access/migrations/0003_auto_20161221_2311.py +++ /dev/null @@ -1,27 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.10.4 on 2016-12-21 23:11 -from __future__ import unicode_literals - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('access', '0002_auto_20161221_1739'), - ] - - operations = [ - migrations.AlterField( - model_name='accessoperator', - name='description', - field=models.TextField(blank=True, default='', verbose_name='description'), - preserve_default=False, - ), - migrations.AlterField( - model_name='accessuser', - name='description', - field=models.TextField(blank=True, default='', max_length=200, verbose_name='description'), - preserve_default=False, - ), - ] diff --git a/src/c3nav/access/migrations/0004_auto_20161223_2225.py b/src/c3nav/access/migrations/0004_auto_20161223_2225.py deleted file mode 100644 index 808fe77d..00000000 --- a/src/c3nav/access/migrations/0004_auto_20161223_2225.py +++ /dev/null @@ -1,19 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.10.4 on 2016-12-23 22:25 -from __future__ import unicode_literals - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('access', '0003_auto_20161221_2311'), - ] - - operations = [ - migrations.AlterModelOptions( - name='accesstokeninstance', - options={'verbose_name': 'Access Token Instance', 'verbose_name_plural': 'Access Token Instances'}, - ), - ] diff --git a/src/c3nav/access/migrations/0005_auto_20161225_1018.py b/src/c3nav/access/migrations/0005_auto_20161225_1018.py deleted file mode 100644 index 5c1c46f3..00000000 --- a/src/c3nav/access/migrations/0005_auto_20161225_1018.py +++ /dev/null @@ -1,22 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.10.4 on 2016-12-25 10:18 -from __future__ import unicode_literals - -from django.conf import settings -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('access', '0004_auto_20161223_2225'), - ] - - operations = [ - migrations.AlterField( - model_name='accessuser', - name='author', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL, verbose_name='creator'), - ), - ] diff --git a/src/c3nav/access/migrations/__init__.py b/src/c3nav/access/migrations/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/src/c3nav/access/models.py b/src/c3nav/access/models.py deleted file mode 100644 index 4fed824d..00000000 --- a/src/c3nav/access/models.py +++ /dev/null @@ -1,126 +0,0 @@ -import string -from datetime import timedelta - -from django.contrib.auth.models import User -from django.db import models, transaction -from django.db.models import Q -from django.urls import reverse -from django.utils import timezone -from django.utils.crypto import get_random_string -from django.utils.functional import cached_property -from django.utils.translation import ugettext_lazy as _ - - -class AccessOperator(models.Model): - user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='operator') - description = models.TextField(_('description'), blank=True) - can_award_permissions = models.CharField(_('can award permissions'), max_length=2048) - access_from = models.DateTimeField(_('has access from'), null=True, blank=True) - access_until = models.DateTimeField(_('has access until'), null=True, blank=True) - - class Meta: - verbose_name = _('Access Operator') - verbose_name_plural = _('Access Operator') - - def __str__(self): - return str(self.user) - - -class AccessUser(models.Model): - user_url = models.CharField(_('access name'), unique=True, max_length=200, - help_text=_('Usually an URL to a profile somewhere')) - author = models.ForeignKey(User, on_delete=models.PROTECT, null=True, blank=True, - verbose_name=_('creator')) - description = models.TextField(_('description'), max_length=200, blank=True) - creation_date = models.DateTimeField(_('creation date'), auto_now_add=True) - - class Meta: - verbose_name = _('Access User') - verbose_name_plural = _('Access Users') - - @property - def valid_tokens(self): - return self.tokens.filter(Q(expired=False) | Q(expires__isnull=False, expires__lt=timezone.now())) - - def new_token(self, **kwargs): - kwargs['secret'] = AccessToken.create_secret() - return self.tokens.create(**kwargs) - - def __str__(self): - return self.user_url - - -class AccessToken(models.Model): - user = models.ForeignKey(AccessUser, on_delete=models.CASCADE, related_name='tokens', - verbose_name=_('Access User')) - author = models.ForeignKey(User, on_delete=models.PROTECT, verbose_name=_('creator'), null=True, blank=True) - permissions = models.CharField(_('permissions'), max_length=2048) - description = models.CharField(_('description'), max_length=200) - creation_date = models.DateTimeField(_('creation date'), auto_now_add=True) - expires = models.DateTimeField(null=True, blank=True) - expired = models.BooleanField(_('is expired'), default=False) - activated = models.BooleanField(_('activated'), default=False) - secret = models.CharField(_('activation secret'), max_length=42) - - class Meta: - verbose_name = _('Access Token') - verbose_name_plural = _('Access Tokens') - - @cached_property - def permissions_list(self): - return self.permissions.split(';') - - @cached_property - def permissions_list_objects(self): - return AreaLocation.objects.filter(name__in=self.permissions_list) - - @cached_property - def full_access(self): - return ':full' in self.permissions_list - - @property - def is_expired(self): - return self.expired or (self.expires is not None and self.expires < timezone.now()) - - @property - def activation_url(self): - if self.activated: - return None - return reverse('access.activate', kwargs={'pk': self.pk, 'secret': self.secret}) - - @property - def qr_url(self): - if self.activated: - return None - return reverse('access.qr', kwargs={'pk': self.pk, 'secret': self.secret}) - - @staticmethod - def create_secret(): - return get_random_string(42, string.ascii_letters + string.digits) - - def new_instance(self): - with transaction.atomic(): - for instance in self.instances.filter(expires__isnull=True): - instance.expires = timezone.now()+timedelta(seconds=5) - instance.save() - - self.instances.filter(expires__isnull=False, expires__lt=timezone.now()).delete() - - secret = self.create_secret() - self.instances.create(secret=secret) - return '%d:%s' % (self.pk, secret) - - def __str__(self): - return '%s #%d' % (_('Access Token'), self.id) - - -class AccessTokenInstance(models.Model): - access_token = models.ForeignKey(AccessToken, on_delete=models.CASCADE, related_name='instances', - verbose_name=_('Access Token')) - secret = models.CharField(_('access secret'), max_length=42) - creation_date = models.DateTimeField(_('creation date'), auto_now_add=True) - expires = models.DateTimeField(null=True) - - class Meta: - verbose_name = _('Access Token Instance') - verbose_name_plural = _('Access Token Instances') diff --git a/src/c3nav/access/static/access/css/c3nav-access.css b/src/c3nav/access/static/access/css/c3nav-access.css deleted file mode 100644 index 04c01547..00000000 --- a/src/c3nav/access/static/access/css/c3nav-access.css +++ /dev/null @@ -1,44 +0,0 @@ -body, .btn { - font-size:16px; -} - -h1 { - margin-bottom:20px; -} - -.login .container { - max-width:420px; -} -.pager .middle { - top:3px; - position:relative; -} - - -footer { - text-align:center; -} -.languages { - margin-bottom:0; - height:2px; -} -.languages button { - text-transform:none; - font-weight:normal; - margin-top:0 !important; - border-width:0 !important; - padding:0; -} - - -.table .btn { - font-size:14px; -} -.table tbody tr td { - min-height: 29px; - line-height:29px; -} - -.token img { - width:100%; -} diff --git a/src/c3nav/access/templates/access/activate.html b/src/c3nav/access/templates/access/activate.html deleted file mode 100644 index 0f7b24e8..00000000 --- a/src/c3nav/access/templates/access/activate.html +++ /dev/null @@ -1,16 +0,0 @@ -{% extends 'access/base.html' %} - -{% load bootstrap3 %} -{% load i18n %} - -{% block bodyclass %}login{% endblock %} - -{% block content %} - {% if success %} -
- {% trans 'Your access token was installed on your device!' %} -
- {% else %} - {% include 'access/fragment_token.html' %} - {% endif %} -{% endblock %} diff --git a/src/c3nav/access/templates/access/base.html b/src/c3nav/access/templates/access/base.html deleted file mode 100644 index 28454a7a..00000000 --- a/src/c3nav/access/templates/access/base.html +++ /dev/null @@ -1,33 +0,0 @@ -{% load static %} -{% load compress %} - - - - - - - c3nav control panel - {% compress css %} - - - {% endcompress %} - - - - -
-

c3nav access control

- {% block nav %} - {% endblock %} - - {% block content %} - {% endblock %} - - {% include 'site/footer.html' %} -
- - {% compress js %} - - {% endcompress %} - - diff --git a/src/c3nav/access/templates/access/fragment_token.html b/src/c3nav/access/templates/access/fragment_token.html deleted file mode 100644 index 79b54419..00000000 --- a/src/c3nav/access/templates/access/fragment_token.html +++ /dev/null @@ -1,17 +0,0 @@ -{% load i18n %} - -
-{% csrf_token %} -
-

- -

-

{% trans 'Link to this token / in this QR code' %}

-

- -

-

- {% blocktrans %}This link QR Code / Link only works once. If you want access on multiple devices, you need multiple tokens.{% endblocktrans %} -

-
-
diff --git a/src/c3nav/access/templates/access/loggedin_base.html b/src/c3nav/access/templates/access/loggedin_base.html deleted file mode 100644 index c551ed29..00000000 --- a/src/c3nav/access/templates/access/loggedin_base.html +++ /dev/null @@ -1,10 +0,0 @@ -{% extends 'access/base.html' %} - -{% load i18n %} - -{% block nav %} - -{% endblock %} diff --git a/src/c3nav/access/templates/access/login.html b/src/c3nav/access/templates/access/login.html deleted file mode 100644 index e665e360..00000000 --- a/src/c3nav/access/templates/access/login.html +++ /dev/null @@ -1,24 +0,0 @@ -{% extends 'access/base.html' %} - -{% load bootstrap3 %} -{% load i18n %} - -{% block bodyclass %}login{% endblock %} - -{% block content %} -
- {% csrf_token %} -
- {% trans 'Log in' %} - {% bootstrap_form form %} -
- -
-
-

{% blocktrans %}This is the non-public backend for creation of access tokens.{% endblocktrans %}

- -

- {% blocktrans %}Prove that you should have access{% endblocktrans %} -

-
-{% endblock %} diff --git a/src/c3nav/access/templates/access/user.html b/src/c3nav/access/templates/access/user.html deleted file mode 100644 index e58d3c8e..00000000 --- a/src/c3nav/access/templates/access/user.html +++ /dev/null @@ -1,83 +0,0 @@ - - - -{% extends 'access/loggedin_base.html' %} - -{% load bootstrap3 %} -{% load i18n %} - -{% block content %} -

Access Tokens {{ user.user_url }}

-

{{ user.description }}

-
- -
-
-

{% trans 'Add new access token' %}

-
- {% csrf_token %} - {% bootstrap_form new_token_form form_group_class='form-group col-md-3' %} -
- - -
-
-
-
- -
-
- {% csrf_token %} - - - - - - - - - - - - - - - {% for token in tokens %} - - - - - - - - - - {% endfor %} - -
{% trans 'ID' %}{% trans 'Description' %}{% trans 'Author' %}{% trans 'Permissions' %}{% trans 'Creation Date' %}{% trans 'Expiration' %}{% trans 'Action' %}
{{ token.id }}{{ token.description }}{% if token.author %}{{ token.author }}{% endif %} - {% if token.full_access %} - all - {% else %} - {% for location in token.permissions_list_objects %} - {{ location.title }}, - {% endfor %} - {% endif %} - {{ token.creation_date|date:"SHORT_DATETIME_FORMAT" }} - {% if token.is_expired %} - {% trans 'expired' %} - {% elif token.expires %} - {{ token.expires|date:"SHORT_DATETIME_FORMAT" }} - {% else %} - {% trans 'never' %} - {% endif %} - - {% if not token.is_expired %} - - {% if not token.activated %} - {% trans 'Activate' %} - {% endif %} - {% endif %} -
-
- -{% endblock %} diff --git a/src/c3nav/access/templates/access/user_token.html b/src/c3nav/access/templates/access/user_token.html deleted file mode 100644 index 60d35cd9..00000000 --- a/src/c3nav/access/templates/access/user_token.html +++ /dev/null @@ -1,13 +0,0 @@ -{% extends 'access/loggedin_base.html' %} - -{% load bootstrap3 %} -{% load i18n %} - -{% block content %} -
-
- {% include 'access/fragment_token.html' %} -
-
-

{% trans 'back to user' %}

-{% endblock %} diff --git a/src/c3nav/access/templates/access/users.html b/src/c3nav/access/templates/access/users.html deleted file mode 100644 index 39291560..00000000 --- a/src/c3nav/access/templates/access/users.html +++ /dev/null @@ -1,70 +0,0 @@ -{% extends 'access/loggedin_base.html' %} - -{% load bootstrap3 %} -{% load i18n %} - -{% block content %} -

Users

- - - - - - - - - - - - - - {% for user in users %} - - - - - - - - - - {% endfor %} - -
{% trans 'ID' %}{% trans 'Name' %}{% trans 'Author' %}{% trans 'Description' %}{% trans 'Active Tokens' %}{% trans 'Creation Date' %}{% trans 'Details' %}
{{ user.id }}{{ user.user_url }}{% if user.author %}{{ user.author }}{% endif %}{{ user.description }}{{ user.valid_tokens.count }}{{ user.creation_date|date:"SHORT_DATETIME_FORMAT" }}{% trans 'Details' %}
- - - -
- -
-
-

{% trans 'Add new access user' %}

-
- {% csrf_token %} - {% bootstrap_form new_user_form form_group_class='form-group col-md-4' %} -
- - -
-
-
-
- -
-{% endblock %} diff --git a/src/c3nav/access/urls.py b/src/c3nav/access/urls.py deleted file mode 100644 index 6f59e012..00000000 --- a/src/c3nav/access/urls.py +++ /dev/null @@ -1,16 +0,0 @@ -from django.conf.urls import url -from django.contrib.auth import views as auth_views - -from c3nav.access.views import activate_token, dashboard, show_user_token, token_qr, user_detail, user_list - -urlpatterns = [ - url(r'^$', dashboard, name='access.dashboard'), - url(r'^activate/(?P[0-9]+):(?P[a-zA-Z0-9]+)/$', activate_token, name='access.activate'), - url(r'^qr/(?P[0-9]+):(?P[a-zA-Z0-9]+).png$', token_qr, name='access.qr'), - url(r'^users/$', user_list, name='access.users'), - url(r'^users/(?P[0-9]+)/$', user_list, name='access.users'), - url(r'^user/(?P[0-9]+)/$', user_detail, name='access.user'), - url(r'^user/(?P[0-9]+)/(?P[0-9]+)/$', show_user_token, name='access.user.token'), - url(r'^login/$', auth_views.login, {'template_name': 'access/login.html'}, name='access.login'), - url(r'^logout/$', auth_views.logout, name='access.logout'), -] diff --git a/src/c3nav/access/views.py b/src/c3nav/access/views.py deleted file mode 100644 index afdece76..00000000 --- a/src/c3nav/access/views.py +++ /dev/null @@ -1,118 +0,0 @@ -import qrcode -from django.contrib.auth.decorators import login_required -from django.core.paginator import EmptyPage, PageNotAnInteger, Paginator -from django.http import HttpResponse -from django.shortcuts import get_object_or_404, redirect, render -from django.urls import reverse - -from c3nav.access.forms import AccessTokenForm, AccessUserForm -from c3nav.access.models import AccessToken, AccessUser - - -@login_required(login_url='/access/login/') -def dashboard(request): - return redirect('access.users') - - -def activate_token(request, pk, secret): - token = get_object_or_404(AccessToken, expired=False, activated=False, id=pk, secret=secret) - if request.method == 'POST': - request.c3nav_access = token - request.c3nav_new_access = True - return render(request, 'access/activate.html', context={ - 'success': True, - }) - - return render(request, 'access/activate.html', context={ - 'token': token, - }) - - -def token_qr(request, pk, secret): - get_object_or_404(AccessToken, expired=False, activated=False, id=pk, secret=secret) - - qr = qrcode.QRCode( - version=1, - error_correction=qrcode.constants.ERROR_CORRECT_L, - box_size=10, - border=4, - ) - qr.add_data(request.build_absolute_uri(reverse('access.activate', kwargs={'pk': pk, 'secret': secret}))) - qr.make(fit=True) - - response = HttpResponse(content_type='image/png') - qr.make_image().save(response, 'PNG') - return response - - -@login_required(login_url='/access/login/') -def user_list(request, page=1): - queryset = AccessUser.objects.all() - paginator = Paginator(queryset, 25) - - try: - users = paginator.page(page) - except PageNotAnInteger: - return redirect('access.users') - except EmptyPage: - return redirect('access.users') - - if request.method == 'POST': - new_user_form = AccessUserForm(data=request.POST) - if new_user_form.is_valid(): - user = new_user_form.instance - user.author = request.user - user.save() - - return redirect('access.user', pk=user.id) - else: - new_user_form = AccessUserForm() - - return render(request, 'access/users.html', { - 'users': users, - 'new_user_form': new_user_form, - }) - - -@login_required(login_url='/access/login/') -def user_detail(request, pk): - user = get_object_or_404(AccessUser, id=pk) - - tokens = user.tokens.order_by('-creation_date') - - if request.method == 'POST': - if 'expire' in request.POST: - token = get_object_or_404(AccessToken, user=user, id=request.POST['expire']) - token.expired = True - token.save() - return redirect('access.user', pk=user.id) - - new_token_form = AccessTokenForm(data=request.POST, request=request) - if new_token_form.is_valid(): - token = new_token_form.instance - token.user = user - token.secret = AccessToken.create_secret() - - token.author = request.user - token.save() - - return redirect('access.user.token', user=user.id, token=token.id) - else: - new_token_form = AccessTokenForm(request=request) - - return render(request, 'access/user.html', { - 'user': user, - 'new_token_form': new_token_form, - 'tokens': tokens, - }) - - -@login_required(login_url='/access/login/') -def show_user_token(request, user, token): - user = get_object_or_404(AccessUser, id=user) - token = get_object_or_404(AccessToken, user=user, id=token) - - return render(request, 'access/user_token.html', { - 'user': user, - 'token': token, - }) diff --git a/src/c3nav/editor/views.py b/src/c3nav/editor/views.py index 746512f9..fb41fe9a 100644 --- a/src/c3nav/editor/views.py +++ b/src/c3nav/editor/views.py @@ -3,7 +3,6 @@ from django.core.exceptions import PermissionDenied from django.http.response import Http404 from django.shortcuts import get_object_or_404, redirect, render -from c3nav.access.apply import can_access, filter_queryset_by_access from c3nav.mapdata.models import Section from c3nav.mapdata.models.base import EDITOR_FORM_MODELS diff --git a/src/c3nav/mapdata/inclusion.py b/src/c3nav/mapdata/inclusion.py deleted file mode 100644 index d919ea13..00000000 --- a/src/c3nav/mapdata/inclusion.py +++ /dev/null @@ -1,63 +0,0 @@ -from collections import OrderedDict - -from django.utils.translation import ugettext_lazy as _ - - -def get_default_include_avoid(): - include = set() - avoid = set() - - locations = list(AreaLocation.objects.exclude(routing_inclusion='default')) - - for location in locations: - if location.routing_inclusion != 'allow_avoid': - avoid.add(location.location_id) - - return include, avoid - - -# Todo: add cache -def get_includables_avoidables(request): - includables = [] - avoidables = [] - - locations = list(AreaLocation.objects.exclude(routing_inclusion='default')) - - if request.c3nav_full_access: - includables.append((':nonpublic', _('non-public areas'))) - avoidables.append((':public', _('public areas'))) - - for location in locations: - item = (location.location_id, location.title) - - if location.location_id not in request.c3nav_access_list and not request.c3nav_full_access: - if location.routing_inclusion == 'needs_permission': - continue - - if location.routing_inclusion == 'allow_avoid': - avoidables.append(item) - else: - includables.append(item) - - return OrderedDict(includables), OrderedDict(avoidables) - - -def get_maybe_invisible_areas(): - return AreaLocation.objects.exclude(routing_inclusion='default') - - -def get_maybe_invisible_areas_names(): - return tuple(area.name for area in get_maybe_invisible_areas()) - - -def parse_include_avoid(request, include_input, avoid_input): - includable, avoidable = get_includables_avoidables(request) - include_input = set(include_input) & set(includable) - avoid_input = set(avoid_input) & set(avoidable) - - default_include, default_avoid = get_default_include_avoid() - - include = set(default_include) | include_input - avoid = set(default_avoid) - include_input | avoid_input - - return ':nonpublic' in includable, include, avoid diff --git a/src/c3nav/mapdata/lastupdate.py b/src/c3nav/mapdata/lastupdate.py deleted file mode 100644 index bf01978f..00000000 --- a/src/c3nav/mapdata/lastupdate.py +++ /dev/null @@ -1,33 +0,0 @@ -import os -import pickle -from contextlib import contextmanager - -from django.conf import settings -from django.utils import timezone - -last_mapdata_update_filename = os.path.join(settings.DATA_DIR, 'last_mapdata_update') -last_mapdata_update_decorator_depth = 0 - - -def get_last_mapdata_update(default_now=False): - try: - with open(last_mapdata_update_filename, 'rb') as f: - return pickle.load(f) - except: - return timezone.now() if default_now else None - - -@contextmanager -def set_last_mapdata_update(): - global last_mapdata_update_decorator_depth - if last_mapdata_update_decorator_depth == 0: - try: - os.remove(last_mapdata_update_filename) - except: - pass - last_mapdata_update_decorator_depth += 1 - yield - last_mapdata_update_decorator_depth -= 1 - if last_mapdata_update_decorator_depth == 0: - with open(last_mapdata_update_filename, 'wb') as f: - pickle.dump(timezone.now(), f) diff --git a/src/c3nav/mapdata/utils/cache.py b/src/c3nav/mapdata/utils/cache.py deleted file mode 100644 index 016a16e8..00000000 --- a/src/c3nav/mapdata/utils/cache.py +++ /dev/null @@ -1,97 +0,0 @@ -from calendar import timegm -from collections import OrderedDict -from functools import wraps - -from django.core.cache import cache -from django.db.models import Q -from django.utils.http import http_date -from rest_framework.response import Response as APIResponse -from rest_framework.views import APIView - -from c3nav.mapdata.lastupdate import get_last_mapdata_update - - -def cache_result(cache_key, timeout=900): - def decorator(func): - @wraps(func) - def inner(*args, **kwargs): - last_update = get_last_mapdata_update() - if last_update is None: - return func(*args, **kwargs) - - result = cache.get(cache_key) - if not result: - result = func(*args, **kwargs) - cache.set(cache_key, result, timeout) - return result - return inner - return decorator - - -def cache_mapdata_api_response(timeout=900): - def decorator(func): - @wraps(func) - def inner(self, request, *args, add_cache_key=None, **kwargs): - last_update = get_last_mapdata_update() - if last_update is None: - return func(self, request, *args, **kwargs) - - cache_key = '__'.join(( - 'c3nav__mapdata__api', - last_update.isoformat(), - add_cache_key if add_cache_key is not None else '', - request.accepted_renderer.format if isinstance(self, APIView) else '', - request.path, - )) - - response = cache.get(cache_key) - if not response: - response = func(self, request, *args, **kwargs) - response['Last-Modifed'] = http_date(timegm(last_update.utctimetuple())) - if isinstance(response, APIResponse): - response = self.finalize_response(request, response, *args, **kwargs) - response.render() - if response.status_code < 400: - cache.set(cache_key, response, timeout) - - return response - return inner - return decorator - - -class CachedReadOnlyViewSetMixin(): - def _get_add_cache_key(self, request, add_cache_key=''): - cache_key = add_cache_key - return cache_key - - def list(self, request, *args, **kwargs): - kwargs['add_cache_key'] = self._get_add_cache_key(request, kwargs.get('add_cache_key', '')) - return self._list(request, *args, **kwargs) - - @cache_mapdata_api_response() - def _list(self, request, *args, **kwargs): - return super().list(request, *args, **kwargs) - - def retrieve(self, request, *args, **kwargs): - kwargs['add_cache_key'] = self._get_add_cache_key(request, kwargs.get('add_cache_key', '')) - return self._retrieve(request, *args, **kwargs) - - @cache_mapdata_api_response() - def _retrieve(self, request, *args, **kwargs): - return super().retrieve(request, *args, **kwargs) - - -@cache_result('c3nav__mapdata__sections') -def get_sections_cached(): - from c3nav.mapdata.models.section import Section - return OrderedDict((section.id, section) for section in Section.objects.all()) - - -@cache_result('c3nav__mapdata__bssids') -def get_bssid_areas_cached(): - from c3nav.mapdata.models import AreaLocation - bssids = {} - for area in AreaLocation.objects.filter(~Q(bssids='')): - for bssid in area.bssids.split('\n'): - bssids[bssid.strip()] = area.name - return bssids diff --git a/src/c3nav/mapdata/utils/misc.py b/src/c3nav/mapdata/utils/misc.py index 8dd67528..20dc469e 100644 --- a/src/c3nav/mapdata/utils/misc.py +++ b/src/c3nav/mapdata/utils/misc.py @@ -1,37 +1,3 @@ -import os - -from django.conf import settings -from shapely.geometry import box -from shapely.ops import cascaded_union - -from c3nav.mapdata.utils.cache import cache_result - - -@cache_result('c3nav__mapdata__dimensions') def get_dimensions(): # todo calculate this return (400, 240) - - -@cache_result('c3nav__mapdata__render_dimensions') -def get_render_dimensions(): - width, height = get_dimensions() - return (width * settings.RENDER_SCALE, height * settings.RENDER_SCALE) - - -def get_render_path(filetype, level, mode, public): - return os.path.join(settings.RENDER_ROOT, - '%s%s-level-%s.%s' % (('public-' if public else ''), mode, level, filetype)) - - -def get_public_private_area(level): - from c3nav.mapdata.models import AreaLocation - - width, height = get_dimensions() - everything = box(0, 0, width, height) - needs_permission = [location.geometry - for location in AreaLocation.objects.filter(level=level, - routing_inclusion='needs_permission')] - public_area = level.public_geometries.areas_and_doors.difference(cascaded_union(needs_permission)) - private_area = everything.difference(public_area) - return public_area, private_area diff --git a/src/c3nav/settings.py b/src/c3nav/settings.py index ef2490cb..9a1a5f55 100644 --- a/src/c3nav/settings.py +++ b/src/c3nav/settings.py @@ -138,7 +138,6 @@ INSTALLED_APPS = [ 'c3nav.api', 'rest_framework', 'c3nav.mapdata', - 'c3nav.access', 'c3nav.routing', 'c3nav.site', 'c3nav.editor', @@ -154,7 +153,6 @@ MIDDLEWARE = [ 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', - 'c3nav.access.middleware.AccessTokenMiddleware', ] try: diff --git a/src/c3nav/site/views.py b/src/c3nav/site/views.py index 8c14d23c..dedbd48f 100644 --- a/src/c3nav/site/views.py +++ b/src/c3nav/site/views.py @@ -7,14 +7,7 @@ from django.shortcuts import get_object_or_404, redirect, render from django.urls import reverse from django.utils import timezone -from c3nav.access.apply import get_visible_areas -from c3nav.mapdata.inclusion import get_includables_avoidables, parse_include_avoid -from c3nav.mapdata.lastupdate import get_last_mapdata_update from c3nav.mapdata.models.section import Section -from c3nav.mapdata.utils.cache import get_sections_cached -from c3nav.mapdata.utils.misc import get_dimensions, get_render_path -from c3nav.routing.exceptions import AlreadyThere, NoRouteFound, NotYetRoutable -from c3nav.routing.graph import Graph ctype_mapping = { 'yes': ('up', 'down'), diff --git a/src/c3nav/urls.py b/src/c3nav/urls.py index 9b56feaf..a7f2bca5 100644 --- a/src/c3nav/urls.py +++ b/src/c3nav/urls.py @@ -1,13 +1,11 @@ from django.conf.urls import include, url from django.contrib import admin -import c3nav.access.urls import c3nav.api.urls import c3nav.editor.urls import c3nav.site.urls urlpatterns = [ - url(r'^access/', include(c3nav.access.urls)), url(r'^editor/', include(c3nav.editor.urls)), url(r'^api/', include(c3nav.api.urls, namespace='api')), url(r'^admin/', admin.site.urls),