diff --git a/cookbook/helper/permission_helper.py b/cookbook/helper/permission_helper.py index 21e4c196..fa9e1200 100644 --- a/cookbook/helper/permission_helper.py +++ b/cookbook/helper/permission_helper.py @@ -43,8 +43,7 @@ def has_group_permission(user, groups): return False groups_allowed = get_allowed_groups(groups) if user.is_authenticated: - if (user.is_superuser - | bool(user.groups.filter(name__in=groups_allowed))): + if bool(user.groups.filter(name__in=groups_allowed)): return True return False @@ -59,19 +58,12 @@ def is_object_owner(user, obj): :param obj any object that should be tested :return: true if user is owner of object, false otherwise """ - # TODO this could be improved/cleaned up by adding - # get_owner methods to all models that allow owner checks if not user.is_authenticated: return False - if user.is_superuser: - return True - if owner := getattr(obj, 'created_by', None): - return owner == user - if owner := getattr(obj, 'user', None): - return owner == user - if getattr(obj, 'get_owner', None): + try: return obj.get_owner() == user - return False + except: + return False def is_object_shared(user, obj): @@ -87,8 +79,6 @@ def is_object_shared(user, obj): # share checks for relevant objects if not user.is_authenticated: return False - if user.is_superuser: - return True return user in obj.shared.all() @@ -100,11 +90,7 @@ def share_link_valid(recipe, share): :return: true if a share link with the given recipe and uuid exists """ try: - return ( - True - if ShareLink.objects.filter(recipe=recipe, uuid=share).exists() - else False - ) + return True if ShareLink.objects.filter(recipe=recipe, uuid=share).exists() else False except ValidationError: return False @@ -148,21 +134,11 @@ class OwnerRequiredMixin(object): def dispatch(self, request, *args, **kwargs): if not request.user.is_authenticated: - messages.add_message( - request, - messages.ERROR, - _('You are not logged in and therefore cannot view this page!') - ) - return HttpResponseRedirect( - reverse_lazy('account_login') + '?next=' + request.path - ) + messages.add_message(request, messages.ERROR, _('You are not logged in and therefore cannot view this page!')) + return HttpResponseRedirect(reverse_lazy('account_login') + '?next=' + request.path) else: if not is_object_owner(request.user, self.get_object()): - messages.add_message( - request, - messages.ERROR, - _('You cannot interact with this object as it is not owned by you!') # noqa: E501 - ) + messages.add_message(request, messages.ERROR, _('You cannot interact with this object as it is not owned by you!')) return HttpResponseRedirect(reverse('index')) if self.get_object().get_space() != request.space: diff --git a/cookbook/provider/dropbox.py b/cookbook/provider/dropbox.py index 2c81c905..af3d410e 100644 --- a/cookbook/provider/dropbox.py +++ b/cookbook/provider/dropbox.py @@ -27,7 +27,7 @@ class Dropbox(Provider): try: recipes = r.json() except ValueError: - log_entry = SyncLog(status='ERROR', msg=str(r), sync=monitor) + log_entry = SyncLog(status='ERROR', msg=str(r), sync=monitor, space=monitor.space) log_entry.save() return r @@ -35,14 +35,14 @@ class Dropbox(Provider): # TODO check if has_more is set and import that as well for recipe in recipes['entries']: path = recipe['path_lower'] - if not Recipe.objects.filter(file_path__iexact=path).exists() \ - and not RecipeImport.objects.filter(file_path=path).exists(): # noqa: E501 + if not Recipe.objects.filter(file_path__iexact=path, space=monitor.space).exists() and not RecipeImport.objects.filter(file_path=path, space=monitor.space).exists(): name = os.path.splitext(recipe['name'])[0] new_recipe = RecipeImport( name=name, file_path=path, storage=monitor.storage, - file_uid=recipe['id'] + file_uid=recipe['id'], + space=monitor.space, ) new_recipe.save() import_count += 1 @@ -50,7 +50,8 @@ class Dropbox(Provider): log_entry = SyncLog( status='SUCCESS', msg='Imported ' + str(import_count) + ' recipes', - sync=monitor + sync=monitor, + space=monitor.space, ) log_entry.save() @@ -104,9 +105,7 @@ class Dropbox(Provider): recipe.link = Dropbox.get_share_link(recipe) recipe.save() - response = requests.get( - recipe.link.replace('www.dropbox.', 'dl.dropboxusercontent.') - ) + response = requests.get(recipe.link.replace('www.dropbox.', 'dl.dropboxusercontent.')) return io.BytesIO(response.content) diff --git a/cookbook/provider/local.py b/cookbook/provider/local.py index 1ec79157..1298e3e4 100644 --- a/cookbook/provider/local.py +++ b/cookbook/provider/local.py @@ -18,13 +18,13 @@ class Local(Provider): import_count = 0 for file in files: path = monitor.path + '/' + file - if not Recipe.objects.filter(file_path__iexact=path).exists() \ - and not RecipeImport.objects.filter(file_path=path).exists(): # noqa: E501 + if not Recipe.objects.filter(file_path__iexact=path, space=monitor.space).exists() and not RecipeImport.objects.filter(file_path=path, space=monitor.space).exists(): name = os.path.splitext(file)[0] new_recipe = RecipeImport( name=name, file_path=path, - storage=monitor.storage + storage=monitor.storage, + space=monitor.space, ) new_recipe.save() import_count += 1 @@ -32,7 +32,8 @@ class Local(Provider): log_entry = SyncLog( status='SUCCESS', msg='Imported ' + str(import_count) + ' recipes', - sync=monitor + sync=monitor, + space=monitor.space, ) log_entry.save() diff --git a/cookbook/provider/nextcloud.py b/cookbook/provider/nextcloud.py index 81d1f16a..f11109ed 100644 --- a/cookbook/provider/nextcloud.py +++ b/cookbook/provider/nextcloud.py @@ -34,13 +34,13 @@ class Nextcloud(Provider): import_count = 0 for file in files: path = monitor.path + '/' + file - if not Recipe.objects.filter(file_path__iexact=path).exists() \ - and not RecipeImport.objects.filter(file_path=path).exists(): # noqa: E501 + if not Recipe.objects.filter(file_path__iexact=path, space=monitor.space).exists() and not RecipeImport.objects.filter(file_path=path, space=monitor.space).exists(): name = os.path.splitext(file)[0] new_recipe = RecipeImport( name=name, file_path=path, - storage=monitor.storage + storage=monitor.storage, + space=monitor.space, ) new_recipe.save() import_count += 1 @@ -48,7 +48,8 @@ class Nextcloud(Provider): log_entry = SyncLog( status='SUCCESS', msg='Imported ' + str(import_count) + ' recipes', - sync=monitor + sync=monitor, + space=monitor.space ) log_entry.save() @@ -68,14 +69,7 @@ class Nextcloud(Provider): data = {'path': recipe.file_path, 'shareType': 3} - r = requests.post( - url, - headers=headers, - auth=HTTPBasicAuth( - recipe.storage.username, recipe.storage.password - ), - data=data - ) + r = requests.post(url, headers=headers, auth=HTTPBasicAuth(recipe.storage.username, recipe.storage.password), data=data) response_json = r.json() diff --git a/cookbook/views/data.py b/cookbook/views/data.py index 5425e7dc..77081e5f 100644 --- a/cookbook/views/data.py +++ b/cookbook/views/data.py @@ -27,10 +27,7 @@ from cookbook.tables import SyncTable def sync(request): if request.method == "POST": if not has_group_permission(request.user, ['admin']): - messages.add_message( - request, messages.ERROR, - _('You do not have the required permissions to view this page!') # noqa: E501 - ) + messages.add_message(request, messages.ERROR, _('You do not have the required permissions to view this page!')) return HttpResponseRedirect(reverse('data_sync')) form = SyncForm(request.POST) if form.is_valid(): @@ -38,21 +35,16 @@ def sync(request): new_path.path = form.cleaned_data['path'] new_path.storage = form.cleaned_data['storage'] new_path.last_checked = datetime.now() + new_path.space = request.space new_path.save() return redirect('data_sync') else: form = SyncForm() - monitored_paths = SyncTable(Sync.objects.all()) - RequestConfig( - request, paginate={'per_page': 25} - ).configure(monitored_paths) + monitored_paths = SyncTable(Sync.objects.fitler(space=request.space).all()) + RequestConfig(request, paginate={'per_page': 25}).configure(monitored_paths) - return render( - request, - 'batch/monitor.html', - {'form': form, 'monitored_paths': monitored_paths} - ) + return render(request, 'batch/monitor.html', {'form': form, 'monitored_paths': monitored_paths}) @group_required('user') @@ -62,14 +54,15 @@ def sync_wait(request): @group_required('user') def batch_import(request): - imports = RecipeImport.objects.all() + imports = RecipeImport.objects.filter(space=request.space).all() for new_recipe in imports: recipe = Recipe( name=new_recipe.name, file_path=new_recipe.file_path, storage=new_recipe.storage, file_uid=new_recipe.file_uid, - created_by=request.user + created_by=request.user, + space=request.space ) recipe.save() new_recipe.delete() @@ -85,7 +78,7 @@ def batch_edit(request): word = form.cleaned_data['search'] keywords = form.cleaned_data['keywords'] - recipes = Recipe.objects.filter(name__icontains=word) + recipes = Recipe.objects.filter(name__icontains=word, space=request.space) count = 0 for recipe in recipes: edit = False @@ -125,6 +118,7 @@ def import_url(request): servings=data['servings'], internal=True, created_by=request.user, + space=request.space, ) step = Step.objects.create( @@ -134,11 +128,10 @@ def import_url(request): recipe.steps.add(step) for kw in data['keywords']: - if kw['id'] != "null" \ - and (k := Keyword.objects.filter(id=kw['id']).first()): + if kw['id'] != "null" and (k := Keyword.objects.filter(id=kw['id'], space=request.space).first()): recipe.keywords.add(k) elif data['all_keywords']: - k = Keyword.objects.create(name=kw['text']) + k = Keyword.objects.create(name=kw['text'], space=request.space) recipe.keywords.add(k) for ing in data['recipeIngredient']: @@ -146,12 +139,12 @@ def import_url(request): if ing['ingredient']['text'] != '': ingredient.food, f_created = Food.objects.get_or_create( - name=ing['ingredient']['text'] + name=ing['ingredient']['text'], space=request.space ) if ing['unit'] and ing['unit']['text'] != '': ingredient.unit, u_created = Unit.objects.get_or_create( - name=ing['unit']['text'] + name=ing['unit']['text'], space=request.space ) # TODO properly handle no_amount recipes @@ -202,16 +195,16 @@ class Object(object): @group_required('user') def statistics(request): counts = Object() - counts.recipes = Recipe.objects.count() - counts.keywords = Keyword.objects.count() - counts.recipe_import = RecipeImport.objects.count() - counts.units = Unit.objects.count() - counts.ingredients = Food.objects.count() - counts.comments = Comment.objects.count() + counts.recipes = Recipe.objects.filter(space=request.space).count() + counts.keywords = Keyword.objects.filter(space=request.space).count() + counts.recipe_import = RecipeImport.objects.filter(space=request.space).count() + counts.units = Unit.objects.filter(space=request.space).count() + counts.ingredients = Food.objects.filter(space=request.space).count() + counts.comments = Comment.objects.filter(recipe__space=request.space).count() - counts.recipes_internal = Recipe.objects.filter(internal=True).count() + counts.recipes_internal = Recipe.objects.filter(internal=True, space=request.space).count() counts.recipes_external = counts.recipes - counts.recipes_internal - counts.recipes_no_keyword = Recipe.objects.filter(keywords=None).count() + counts.recipes_no_keyword = Recipe.objects.filter(keywords=None, space=request.space).count() return render(request, 'stats.html', {'counts': counts}) diff --git a/cookbook/views/delete.py b/cookbook/views/delete.py index 689ad2f1..cac4250b 100644 --- a/cookbook/views/delete.py +++ b/cookbook/views/delete.py @@ -31,7 +31,7 @@ class RecipeDelete(GroupRequiredMixin, DeleteView): @group_required('user') def delete_recipe_source(request, pk): - recipe = get_object_or_404(Recipe, pk=pk) + recipe = get_object_or_404(Recipe, pk=pk, space=request.space) if recipe.storage.method == Storage.DROPBOX: # TODO central location to handle storage type switches @@ -130,25 +130,12 @@ class RecipeBookDelete(OwnerRequiredMixin, DeleteView): return context -class RecipeBookEntryDelete(GroupRequiredMixin, DeleteView): +class RecipeBookEntryDelete(OwnerRequiredMixin, DeleteView): groups_required = ['user'] template_name = "generic/delete_template.html" model = RecipeBookEntry success_url = reverse_lazy('view_books') - def dispatch(self, request, *args, **kwargs): - obj = self.get_object() - if not (obj.book.created_by == request.user - or request.user.is_superuser): - messages.add_message( - request, - messages.ERROR, - _('You cannot interact with this object as it is not owned by you!') # noqa: E501 - ) - return HttpResponseRedirect(reverse('index')) - return super(RecipeBookEntryDelete, self) \ - .dispatch(request, *args, **kwargs) - def get_context_data(self, **kwargs): context = super(RecipeBookEntryDelete, self).get_context_data(**kwargs) context['title'] = _("Bookmarks")