diff --git a/src/c3nav/access/apply.py b/src/c3nav/access/apply.py new file mode 100644 index 00000000..4f1c95ce --- /dev/null +++ b/src/c3nav/access/apply.py @@ -0,0 +1,31 @@ +from django.conf import settings + +from c3nav.mapdata.utils.cache import get_packages_cached + + +def get_public_packages(): + packages_cached = get_packages_cached() + return [packages_cached[name] for name in settings.PUBLIC_PACKAGES] + + +def get_nonpublic_packages(): + packages_cached = get_packages_cached() + return [package for name, package in packages_cached.items() if name not in settings.PUBLIC_PACKAGES] + + +def get_unlocked_packages(request, packages_cached=None): + return tuple(get_packages_cached().values()) if request.c3nav_full_access else get_public_packages() + + +def get_unlocked_packages_names(request, packages_cached=None): + if request.c3nav_full_access: + return get_packages_cached().keys() + return settings.PUBLIC_PACKAGES + + +def can_access_package(request, package): + return request.c3nav_full_access or package.name in get_unlocked_packages_names(request) + + +def filter_queryset_by_package_access(request, queryset): + return queryset if request.c3nav_full_access else queryset.filter(package__in=get_unlocked_packages(request)) diff --git a/src/c3nav/access/middleware.py b/src/c3nav/access/middleware.py index 50103bd1..627a8f72 100644 --- a/src/c3nav/access/middleware.py +++ b/src/c3nav/access/middleware.py @@ -17,6 +17,9 @@ class AccessTokenMiddleware: request.c3nav_access = None request.c3nav_new_access = False + request.c3nav_full_access = False + request.c3nav_access_list = None + 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(':') @@ -31,6 +34,9 @@ class AccessTokenMiddleware: 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: diff --git a/src/c3nav/access/models.py b/src/c3nav/access/models.py index e47fe80f..3e47723a 100644 --- a/src/c3nav/access/models.py +++ b/src/c3nav/access/models.py @@ -7,6 +7,7 @@ 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 _ @@ -65,6 +66,14 @@ class AccessToken(models.Model): verbose_name = _('Access Token') verbose_name_plural = _('Access Tokens') + @cached_property + def permissions_list(self): + return self.permissions.split(';') + + @cached_property + def full_access(self): + return ':full' in self.permissions_list + @property def activation_url(self): if self.activated: diff --git a/src/c3nav/access/views.py b/src/c3nav/access/views.py index 05570a46..381b3db0 100644 --- a/src/c3nav/access/views.py +++ b/src/c3nav/access/views.py @@ -4,9 +4,9 @@ from django.contrib.auth.decorators import login_required from django.db import transaction from django.shortcuts import get_object_or_404, render +from c3nav.access.apply import get_nonpublic_packages from c3nav.access.models import AccessToken, AccessUser from c3nav.editor.hosters import get_hoster_for_package -from c3nav.mapdata.permissions import get_nonpublic_packages @login_required(login_url='/access/login/') @@ -54,7 +54,7 @@ def prove(request): else: user = AccessUser.objects.create(user_url=user_id) - token = user.new_token(permissions=':all', description='automatically created') + token = user.new_token(permissions=':full', description='automatically created') return render(request, 'access/prove.html', context={ 'hosters': hosters, 'success': True, diff --git a/src/c3nav/editor/forms.py b/src/c3nav/editor/forms.py index 4861be0c..d2ef6dd3 100644 --- a/src/c3nav/editor/forms.py +++ b/src/c3nav/editor/forms.py @@ -9,8 +9,8 @@ from django.forms.widgets import HiddenInput from django.utils.translation import ugettext_lazy as _ from shapely.geometry.geo import mapping +from c3nav.access.apply import get_unlocked_packages from c3nav.mapdata.models import Package -from c3nav.mapdata.permissions import get_unlocked_packages class MapitemFormMixin(ModelForm): diff --git a/src/c3nav/editor/views.py b/src/c3nav/editor/views.py index 20f206a2..bcc0171e 100644 --- a/src/c3nav/editor/views.py +++ b/src/c3nav/editor/views.py @@ -6,12 +6,12 @@ from django.http.response import Http404 from django.shortcuts import get_object_or_404, redirect, render from django.utils import translation +from c3nav.access.apply import can_access_package, filter_queryset_by_package_access from c3nav.editor.hosters import get_hoster_for_package, hosters from c3nav.mapdata.models import AreaLocation from c3nav.mapdata.models.base import MAPITEM_TYPES from c3nav.mapdata.models.package import Package from c3nav.mapdata.packageio.write import json_encode -from c3nav.mapdata.permissions import can_access_package, filter_queryset_by_package_access def list_mapitemtypes(request, level): diff --git a/src/c3nav/mapdata/api.py b/src/c3nav/mapdata/api.py index cba231c1..05b0290f 100644 --- a/src/c3nav/mapdata/api.py +++ b/src/c3nav/mapdata/api.py @@ -9,10 +9,10 @@ from rest_framework.decorators import detail_route from rest_framework.response import Response from rest_framework.viewsets import ReadOnlyModelViewSet, ViewSet +from c3nav.access.apply import filter_queryset_by_package_access, get_unlocked_packages_names from c3nav.mapdata.models import GEOMETRY_MAPITEM_TYPES, AreaLocation, Level, LocationGroup, Package, Source from c3nav.mapdata.models.geometry import DirectedLineGeometryMapItemWithLevel from c3nav.mapdata.models.locations import get_location -from c3nav.mapdata.permissions import filter_queryset_by_package_access, get_unlocked_packages_names from c3nav.mapdata.serializers.main import LevelSerializer, PackageSerializer, SourceSerializer from c3nav.mapdata.utils.cache import (CachedReadOnlyViewSetMixin, cache_mapdata_api_response, get_levels_cached, get_packages_cached) diff --git a/src/c3nav/mapdata/inclusion.py b/src/c3nav/mapdata/inclusion.py index 35c430d0..2cf9c518 100644 --- a/src/c3nav/mapdata/inclusion.py +++ b/src/c3nav/mapdata/inclusion.py @@ -3,8 +3,8 @@ from collections import OrderedDict from django.conf import settings from django.utils.translation import ugettext_lazy as _ +from c3nav.access.apply import can_access_package from c3nav.mapdata.models import AreaLocation, LocationGroup -from c3nav.mapdata.permissions import can_access_package def get_default_include_avoid(): diff --git a/src/c3nav/mapdata/models/level.py b/src/c3nav/mapdata/models/level.py index dc8d35c9..2df1cc40 100644 --- a/src/c3nav/mapdata/models/level.py +++ b/src/c3nav/mapdata/models/level.py @@ -86,7 +86,7 @@ class LevelGeometries(): self.level = level self.only_public = only_public - from c3nav.mapdata.permissions import get_public_packages + from c3nav.access.apply import get_public_packages self.public_packages = get_public_packages() def query(self, name): diff --git a/src/c3nav/mapdata/models/locations.py b/src/c3nav/mapdata/models/locations.py index ac4ee9fd..ce06af0f 100644 --- a/src/c3nav/mapdata/models/locations.py +++ b/src/c3nav/mapdata/models/locations.py @@ -8,12 +8,12 @@ from django.db.models import Q from django.utils.functional import cached_property from django.utils.translation import ugettext_lazy as _ +from c3nav.access.apply import filter_queryset_by_package_access from c3nav.mapdata.fields import JSONField from c3nav.mapdata.lastupdate import get_last_mapdata_update from c3nav.mapdata.models import Level from c3nav.mapdata.models.base import MapItem from c3nav.mapdata.models.geometry import GeometryMapItemWithLevel -from c3nav.mapdata.permissions import filter_queryset_by_package_access from c3nav.mapdata.utils.cache import get_levels_cached diff --git a/src/c3nav/mapdata/permissions.py b/src/c3nav/mapdata/permissions.py deleted file mode 100644 index 913dd559..00000000 --- a/src/c3nav/mapdata/permissions.py +++ /dev/null @@ -1,58 +0,0 @@ -from django.conf import settings -from django.utils.translation import ugettext_lazy as _ -from rest_framework.exceptions import PermissionDenied -from rest_framework.permissions import BasePermission -from shapely.geometry import box - -from c3nav.mapdata.models import Source -from c3nav.mapdata.utils.cache import get_packages_cached -from c3nav.mapdata.utils.misc import get_dimensions - - -def get_public_packages(): - packages_cached = get_packages_cached() - return [packages_cached[name] for name in settings.PUBLIC_PACKAGES] - - -def get_nonpublic_packages(): - packages_cached = get_packages_cached() - return [package for name, package in packages_cached.items() if name not in settings.PUBLIC_PACKAGES] - - -def get_unlocked_packages_names(request, packages_cached=None): - if packages_cached is None: - packages_cached = get_packages_cached() - if settings.DIRECT_EDITING: - return packages_cached.keys() - return set(settings.PUBLIC_PACKAGES) | set(request.session.get('unlocked_packages', ())) - - -def get_unlocked_packages(request, packages_cached=None): - if packages_cached is None: - packages_cached = get_packages_cached() - names = get_unlocked_packages_names(request, packages_cached=packages_cached) - return tuple(packages_cached[name] for name in names if name in packages_cached) - - -def can_access_package(request, package): - return settings.DEBUG or package.name in get_unlocked_packages_names(request) - - -def filter_queryset_by_package_access(request, queryset): - return queryset if settings.DIRECT_EDITING else queryset.filter(package__in=get_unlocked_packages(request)) - - -def get_public_private_area(level): - width, height = get_dimensions() - everything = box(0, 0, width, height) - public_area = level.public_geometries.areas_and_doors - private_area = everything.difference(public_area) - return public_area, private_area - - -class LockedMapFeatures(BasePermission): - def has_object_permission(self, request, view, obj): - if isinstance(obj, Source): - if not can_access_package(request, obj.package): - raise PermissionDenied(_('This Source belongs to a package you don\'t have access to.')) - return True diff --git a/src/c3nav/mapdata/utils/cache.py b/src/c3nav/mapdata/utils/cache.py index a34b5f1e..866092c1 100644 --- a/src/c3nav/mapdata/utils/cache.py +++ b/src/c3nav/mapdata/utils/cache.py @@ -62,7 +62,7 @@ class CachedReadOnlyViewSetMixin(): include_package_access = False def _get_unlocked_packages_ids(self, request): - from c3nav.mapdata.permissions import get_unlocked_packages + from c3nav.access.apply import get_unlocked_packages return ','.join(str(i) for i in sorted(package.id for package in get_unlocked_packages(request))) def _get_add_cache_key(self, request, add_cache_key=''): diff --git a/src/c3nav/mapdata/utils/misc.py b/src/c3nav/mapdata/utils/misc.py index a1668b80..1640ca32 100644 --- a/src/c3nav/mapdata/utils/misc.py +++ b/src/c3nav/mapdata/utils/misc.py @@ -2,6 +2,7 @@ import os from django.conf import settings from django.db.models import Max, Min +from shapely.geometry import box from c3nav.mapdata.models import Package from c3nav.mapdata.utils.cache import cache_result @@ -25,3 +26,11 @@ def get_render_dimensions(): 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): + width, height = get_dimensions() + everything = box(0, 0, width, height) + public_area = level.public_geometries.areas_and_doors + private_area = everything.difference(public_area) + return public_area, private_area diff --git a/src/c3nav/routing/level.py b/src/c3nav/routing/level.py index 1183bda6..9b00384a 100644 --- a/src/c3nav/routing/level.py +++ b/src/c3nav/routing/level.py @@ -10,8 +10,9 @@ from scipy.sparse.csgraph._shortest_path import shortest_path from scipy.sparse.csgraph._tools import csgraph_from_dense from shapely.geometry import CAP_STYLE, JOIN_STYLE, LineString -from c3nav.mapdata.permissions import get_public_packages, get_public_private_area +from c3nav.access.apply import get_public_packages from c3nav.mapdata.utils.geometry import assert_multilinestring, assert_multipolygon +from c3nav.mapdata.utils.misc import get_public_private_area from c3nav.routing.point import GraphPoint from c3nav.routing.room import GraphRoom from c3nav.routing.utils.base import get_nearest_point diff --git a/src/c3nav/site/static/site/css/c3nav.css b/src/c3nav/site/static/site/css/c3nav.css index 0851045c..1214edd3 100644 --- a/src/c3nav/site/static/site/css/c3nav.css +++ b/src/c3nav/site/static/site/css/c3nav.css @@ -264,3 +264,8 @@ circle.pos { background-repeat:no-repeat; background-position:center; } + + +footer { + text-align:center; +} diff --git a/src/c3nav/site/templates/site/base.html b/src/c3nav/site/templates/site/base.html index 0872e776..894d66d3 100644 --- a/src/c3nav/site/templates/site/base.html +++ b/src/c3nav/site/templates/site/base.html @@ -1,5 +1,7 @@ {% load static %} {% load compress %} +{% load i18n %} +
@@ -19,6 +21,27 @@