From bf761c1a1c2d2e378b614d8b4a2b043b9be2acc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Laura=20Kl=C3=BCnder?= Date: Tue, 20 Nov 2018 22:54:29 +0100 Subject: [PATCH] respect base_mapdata_access in editor --- src/c3nav/control/models.py | 14 ++++ src/c3nav/editor/api.py | 7 +- src/c3nav/editor/forms.py | 12 ++- .../editor/fragment_child_models.html | 2 +- src/c3nav/editor/templates/editor/index.html | 2 +- src/c3nav/editor/templates/editor/level.html | 2 +- src/c3nav/editor/templates/editor/list.html | 2 +- src/c3nav/editor/views/base.py | 3 +- src/c3nav/editor/views/edit.py | 74 ++++++++++++++----- 9 files changed, 89 insertions(+), 29 deletions(-) diff --git a/src/c3nav/control/models.py b/src/c3nav/control/models.py index 0f4249c4..f7744aa2 100644 --- a/src/c3nav/control/models.py +++ b/src/c3nav/control/models.py @@ -39,6 +39,15 @@ class UserPermissions(models.Model): def get_cache_key(pk): return 'control:permissions:%d' % pk + @classmethod + def cache_key_for_request(cls): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + if self.user_id and self.user.is_superuser: + for field in UserPermissions._meta.get_fields(): + if isinstance(field, models.BooleanField): + setattr(self, field.name, True) + @classmethod def get_for_user(cls, user, force=False) -> 'UserPermissions': if not user.is_authenticated: @@ -65,5 +74,10 @@ class UserPermissions(models.Model): cache_key = self.get_cache_key(self.pk) cache.set(cache_key, self, 900) + @property + def can_access_base_mapdata(self): + return False + return settings.PUBLIC_BASE_MAPDATA or self.base_mapdata_access + get_permissions_for_user_lazy = lazy(UserPermissions.get_for_user, UserPermissions) diff --git a/src/c3nav/editor/api.py b/src/c3nav/editor/api.py index 1c43298b..ce3501e8 100644 --- a/src/c3nav/editor/api.py +++ b/src/c3nav/editor/api.py @@ -73,7 +73,10 @@ class EditorViewSet(ViewSet): @api_etag(etag_func=etag_func, cache_parameters={'level': str, 'space': str}) def geometries(self, request, *args, **kwargs): if not can_access_editor(request): - return PermissionDenied + raise PermissionDenied + + if not request.user_permissions.can_access_base_mapdata: + raise PermissionDenied Level = request.changeset.wrap_model('Level') Space = request.changeset.wrap_model('Space') @@ -214,7 +217,7 @@ class EditorViewSet(ViewSet): @api_etag(etag_func=MapUpdate.current_cache_key, cache_parameters={}) def geometrystyles(self, request, *args, **kwargs): if not can_access_editor(request): - return PermissionDenied + raise PermissionDenied return Response({ 'building': '#aaaaaa', diff --git a/src/c3nav/editor/forms.py b/src/c3nav/editor/forms.py index 7ccd1b62..334b3164 100644 --- a/src/c3nav/editor/forms.py +++ b/src/c3nav/editor/forms.py @@ -41,10 +41,14 @@ class EditorFormBase(I18nModelFormMixin, ModelForm): self.fields['space'].widget = HiddenInput() if 'geometry' in self.fields: - # hide geometry widget - self.fields['geometry'].widget = HiddenInput() - if not creating: - self.initial['geometry'] = json.dumps(mapping(self.instance.geometry), separators=(',', ':')) + if not request.user_permissions.can_access_base_mapdata: + # can't see this geometry in editor + self.fields.pop('geometry') + else: + # hide geometry widget + self.fields['geometry'].widget = HiddenInput() + if not creating: + self.initial['geometry'] = json.dumps(mapping(self.instance.geometry), separators=(',', ':')) if self._meta.model.__name__ == 'Source' and self.request.user.is_superuser: Source = self.request.changeset.wrap_model('Source') diff --git a/src/c3nav/editor/templates/editor/fragment_child_models.html b/src/c3nav/editor/templates/editor/fragment_child_models.html index 5e75a82d..3ddd6e08 100644 --- a/src/c3nav/editor/templates/editor/fragment_child_models.html +++ b/src/c3nav/editor/templates/editor/fragment_child_models.html @@ -7,7 +7,7 @@ {{ model.title }} {% endfor %} - {% if graph_url %} + {% if can_edit_graph and graph_url %} {% trans 'Graph' %} diff --git a/src/c3nav/editor/templates/editor/index.html b/src/c3nav/editor/templates/editor/index.html index 1e3024f7..26ebf755 100644 --- a/src/c3nav/editor/templates/editor/index.html +++ b/src/c3nav/editor/templates/editor/index.html @@ -4,7 +4,7 @@

{% trans 'c3nav map editor' %}

{% bootstrap_messages %} -{% if can_edit %} +{% if can_create_level %}

{% trans 'Level' as model_title %} diff --git a/src/c3nav/editor/templates/editor/level.html b/src/c3nav/editor/templates/editor/level.html index 3d3e2aa2..a0834c39 100644 --- a/src/c3nav/editor/templates/editor/level.html +++ b/src/c3nav/editor/templates/editor/level.html @@ -32,7 +32,7 @@ {% if level.on_top_of is None %}

{% trans 'Levels on top' %}

- {% if can_edit %} + {% if can_create_level %}

{% blocktrans %}New {{ model_title }}{% endblocktrans %} diff --git a/src/c3nav/editor/templates/editor/list.html b/src/c3nav/editor/templates/editor/list.html index 98ab7f56..897d57bd 100644 --- a/src/c3nav/editor/templates/editor/list.html +++ b/src/c3nav/editor/templates/editor/list.html @@ -16,7 +16,7 @@ {% endif %} {% bootstrap_messages %} -{% if can_edit %} +{% if can_create_level %} {% blocktrans %}New {{ model_title }}{% endblocktrans %} diff --git a/src/c3nav/editor/views/base.py b/src/c3nav/editor/views/base.py index d959e2f2..14c7945c 100644 --- a/src/c3nav/editor/views/base.py +++ b/src/c3nav/editor/views/base.py @@ -56,4 +56,5 @@ def etag_func(request, *args, **kwargs): request.changeset = changeset return (get_language() + ':' + changeset.raw_cache_key_by_changes + ':' + - AccessPermission.cache_key_for_request(request, with_update=False) + ':' + str(request.user.pk or 0)) + AccessPermission.cache_key_for_request(request, with_update=False) + ':' + + str(request.user.pk or 0) + ':' + str(int(request.user_permissions.can_access_base_mapdata))) diff --git a/src/c3nav/editor/views/edit.py b/src/c3nav/editor/views/edit.py index 24b0b3f2..89a8857d 100644 --- a/src/c3nav/editor/views/edit.py +++ b/src/c3nav/editor/views/edit.py @@ -45,7 +45,8 @@ def main_index(request): Level = request.changeset.wrap_model('Level') return render(request, 'editor/index.html', { 'levels': Level.objects.filter(Level.q_for_request(request), on_top_of__isnull=True), - 'can_edit': request.changeset.can_edit(request), + 'can_create_level': (request.user_permissions.can_access_base_mapdata and + request.changeset.can_edit(request)), 'child_models': [ child_model(request, 'LocationGroupCategory'), child_model(request, 'LocationGroup'), @@ -64,17 +65,25 @@ def level_detail(request, pk): qs = Level.objects.filter(Level.q_for_request(request)) level = get_object_or_404(qs.select_related('on_top_of').prefetch_related('levels_on_top'), pk=pk) + if request.user_permissions.can_access_base_mapdata: + submodels = ('Building', 'Space', 'Door') + else: + submodels = ('Space', ) + return render(request, 'editor/level.html', { 'levels': Level.objects.filter(Level.q_for_request(request), on_top_of__isnull=True), 'level': level, 'level_url': 'editor.levels.detail', 'level_as_pk': True, - 'can_edit': request.changeset.can_edit(request), + 'can_edit_graph': request.user_permissions.can_access_base_mapdata, + 'can_create_level': (request.user_permissions.can_access_base_mapdata and + request.changeset.can_edit(request)), 'child_models': [child_model(request, model_name, kwargs={'level': pk}, parent=level) - for model_name in ('Building', 'Space', 'Door')], + for model_name in submodels], 'levels_on_top': level.levels_on_top.filter(Level.q_for_request(request)).all(), - 'geometry_url': '/api/editor/geometries/?level='+str(level.primary_level_pk), + 'geometry_url': ('/api/editor/geometries/?level='+str(level.primary_level_pk) + if request.user_permissions.can_access_base_mapdata else None), }) @@ -86,18 +95,24 @@ def space_detail(request, level, pk): qs = Space.objects.filter(Space.q_for_request(request)) space = get_object_or_404(qs.select_related('level'), level__pk=level, pk=pk) + if request.user_permissions.can_access_base_mapdata: + submodels = ('POI', 'Area', 'Obstacle', 'LineObstacle', 'Stair', 'Ramp', 'Column', + 'Hole', 'AltitudeMarker', 'LeaveDescription', 'CrossDescription', + 'WifiMeasurement') + else: + submodels = ('POI', 'Area', 'AltitudeMarker', 'LeaveDescription', 'CrossDescription') + return render(request, 'editor/space.html', { 'levels': Level.objects.filter(Level.q_for_request(request), on_top_of__isnull=True), 'level': space.level, 'level_url': 'editor.spaces.list', 'space': space, - 'can_edit': request.changeset.can_edit(request), + 'can_edit_graph': request.user_permissions.can_access_base_mapdata, 'child_models': [child_model(request, model_name, kwargs={'space': pk}, parent=space) - for model_name in ('POI', 'Area', 'Obstacle', 'LineObstacle', 'Stair', 'Ramp', 'Column', - 'Hole', 'AltitudeMarker', 'LeaveDescription', 'CrossDescription', - 'WifiMeasurement')], - 'geometry_url': '/api/editor/geometries/?space='+pk, + for model_name in submodels], + 'geometry_url': ('/api/editor/geometries/?space='+pk + if request.user_permissions.can_access_base_mapdata else None), }) @@ -121,6 +136,9 @@ def edit(request, pk=None, model=None, level=None, space=None, on_top_of=None, e can_edit = request.changeset.can_edit(request) + if pk is None and not request.user_permissions.can_access_base_mapdata: + raise PermissionDenied + obj = None if pk is not None: # Edit existing map item @@ -131,9 +149,13 @@ def edit(request, pk=None, model=None, level=None, space=None, on_top_of=None, e if level is not None: kwargs.update({'level__pk': level}) qs = qs.select_related('level') + can_edit = False elif space is not None: kwargs.update({'space__pk': space}) qs = qs.select_related('space') + else: + if not request.user_permissions.can_access_base_mapdata: + can_edit = False obj = get_object_or_404(qs, **kwargs) elif level is not None: level = get_object_or_404(Level.objects.filter(Level.q_for_request(request)), pk=level) @@ -144,6 +166,7 @@ def edit(request, pk=None, model=None, level=None, space=None, on_top_of=None, e pk=on_top_of) new = obj is None + # noinspection PyProtectedMember ctx = { 'path': request.path, @@ -169,12 +192,14 @@ def edit(request, pk=None, model=None, level=None, space=None, on_top_of=None, e }) if not new: ctx.update({ - 'geometry_url': '/api/editor/geometries/?level='+str(obj.primary_level_pk), + 'geometry_url': ('/api/editor/geometries/?level='+str(obj.primary_level_pk) + if request.user_permissions.can_access_base_mapdata else None), 'on_top_of': obj.on_top_of, }) elif on_top_of: ctx.update({ - 'geometry_url': '/api/editor/geometries/?level=' + str(on_top_of.pk), + 'geometry_url': ('/api/editor/geometries/?level=' + str(on_top_of.pk) + if request.user_permissions.can_access_base_mapdata else None), 'on_top_of': on_top_of, 'back_url': reverse('editor.levels.detail', kwargs={'pk': on_top_of.pk}), }) @@ -183,14 +208,16 @@ def edit(request, pk=None, model=None, level=None, space=None, on_top_of=None, e ctx.update({ 'level': obj.level, 'back_url': reverse('editor.spaces.detail', kwargs={'level': obj.level.pk, 'pk': pk}), - 'geometry_url': '/api/editor/geometries/?space='+pk, + 'geometry_url': ('/api/editor/geometries/?space='+pk + if request.user_permissions.can_access_base_mapdata else None), 'nozoom': True, }) elif model == Space and new: ctx.update({ 'level': level, 'back_url': reverse('editor.spaces.list', kwargs={'level': level.pk}), - 'geometry_url': '/api/editor/geometries/?level='+str(level.primary_level_pk), + 'geometry_url': ('/api/editor/geometries/?level='+str(level.primary_level_pk) + if request.user_permissions.can_access_base_mapdata else None), 'nozoom': True, }) elif hasattr(model, 'level'): @@ -199,7 +226,8 @@ def edit(request, pk=None, model=None, level=None, space=None, on_top_of=None, e ctx.update({ 'level': level, 'back_url': reverse('editor.'+related_name+'.list', kwargs={'level': level.pk}), - 'geometry_url': '/api/editor/geometries/?level='+str(level.primary_level_pk), + 'geometry_url': ('/api/editor/geometries/?level='+str(level.primary_level_pk) + if request.user_permissions.can_access_base_mapdata else None), }) elif hasattr(model, 'space'): if not new: @@ -208,7 +236,8 @@ def edit(request, pk=None, model=None, level=None, space=None, on_top_of=None, e ctx.update({ 'level': space.level, 'back_url': reverse('editor.'+related_name+'.list', kwargs={'space': space.pk}), - 'geometry_url': '/api/editor/geometries/?space='+str(space.pk), + 'geometry_url': ('/api/editor/geometries/?space='+str(space.pk) + if request.user_permissions.can_access_base_mapdata else None), }) else: kwargs = {} @@ -357,6 +386,7 @@ def list_objects(request, model=None, level=None, space=None, explicit_edit=Fals Space = request.changeset.wrap_model('Space') can_edit = request.changeset.can_edit(request) + can_create = request.user_permissions.can_access_base_mapdata and can_edit ctx = { 'path': request.path, @@ -364,7 +394,7 @@ def list_objects(request, model=None, level=None, space=None, explicit_edit=Fals 'model_title': model._meta.verbose_name, 'model_title_plural': model._meta.verbose_name_plural, 'explicit_edit': explicit_edit, - 'can_edit': can_edit, + 'can_create': can_create, } queryset = model.objects.all().order_by('id') @@ -382,7 +412,8 @@ def list_objects(request, model=None, level=None, space=None, explicit_edit=Fals 'levels': Level.objects.filter(Level.q_for_request(request), on_top_of__isnull=True), 'level': level, 'level_url': request.resolver_match.url_name, - 'geometry_url': '/api/editor/geometries/?level='+str(level.primary_level_pk), + 'geometry_url': ('/api/editor/geometries/?level='+str(level.primary_level_pk) + if request.user_permissions.can_access_base_mapdata else None), }) elif space is not None: reverse_kwargs['space'] = space @@ -418,7 +449,8 @@ def list_objects(request, model=None, level=None, space=None, explicit_edit=Fals 'space': space, 'back_url': reverse('editor.spaces.detail', kwargs={'level': space.level.pk, 'pk': space.pk}), 'back_title': _('back to space'), - 'geometry_url': '/api/editor/geometries/?space='+str(space.pk), + 'geometry_url': ('/api/editor/geometries/?space='+str(space.pk) + if request.user_permissions.can_access_base_mapdata else None), }) else: ctx.update({ @@ -441,6 +473,9 @@ def list_objects(request, model=None, level=None, space=None, explicit_edit=Fals def connect_nodes(request, active_node, clicked_node, edge_settings_form): + if not request.user_permissions.can_access_base_mapdata: + raise PermissionDenied + changeset_exceeded = get_changeset_exceeded(request) graphedge_changes = {} if changeset_exceeded: @@ -476,6 +511,9 @@ def connect_nodes(request, active_node, clicked_node, edge_settings_form): @sidebar_view @etag(etag_func) def graph_edit(request, level=None, space=None): + if not request.user_permissions.can_access_base_mapdata: + raise PermissionDenied + Level = request.changeset.wrap_model('Level') Space = request.changeset.wrap_model('Space') GraphNode = request.changeset.wrap_model('GraphNode')