From 81b41529d2b6864ab69fe25c7fb46cab51d4ee94 Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Mon, 9 May 2016 11:50:49 +0200 Subject: [PATCH 1/4] US #2929: Ability to edit commit and see their history --- CHANGELOG.md | 4 + taiga/projects/history/api.py | 84 +- .../migrations/0009_auto_20160512_1110.py | 26 + taiga/projects/history/models.py | 18 + taiga/projects/history/permissions.py | 27 +- taiga/projects/history/serializers.py | 2 + taiga/projects/history/services.py | 14 + taiga/timeline/signals.py | 5 +- .../test_history_resources.py | 897 +++++++++++++++++- tests/integration/test_history.py | 83 ++ 10 files changed, 1102 insertions(+), 58 deletions(-) create mode 100644 taiga/projects/history/migrations/0009_auto_20160512_1110.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b5f288a..eca3114a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog # +## 2.2.0 ??? (unreleased) +### Features +- [API] edit comment endpoint: comment owners and project admins can edit existing comments + ## 2.1.0 Ursus Americanus (2016-05-03) diff --git a/taiga/projects/history/api.py b/taiga/projects/history/api.py index d10194ce..3f1ca240 100644 --- a/taiga/projects/history/api.py +++ b/taiga/projects/history/api.py @@ -23,6 +23,7 @@ from taiga.base import response from taiga.base.decorators import detail_route from taiga.base.api import ReadOnlyListViewSet from taiga.base.api.utils import get_object_or_404 +from taiga.mdrender.service import render as mdrender from . import permissions from . import serializers @@ -56,42 +57,93 @@ class HistoryViewSet(ReadOnlyListViewSet): return response.Ok(serializer.data) + @detail_route(methods=['get']) + def comment_versions(self, request, pk): + obj = self.get_object() + history_entry_id = request.QUERY_PARAMS.get('id', None) + history_entry = services.get_history_queryset_by_model_instance(obj).filter(id=history_entry_id).first() + + self.check_permissions(request, 'comment_versions', history_entry) + + if history_entry is None: + return response.NotFound() + + history_entry.attach_user_info_to_comment_versions() + return response.Ok(history_entry.comment_versions) + + @detail_route(methods=['post']) + def edit_comment(self, request, pk): + obj = self.get_object() + history_entry_id = request.QUERY_PARAMS.get('id', None) + history_entry = services.get_history_queryset_by_model_instance(obj).filter(id=history_entry_id).first() + obj = services.get_instance_from_key(history_entry.key) + comment = request.DATA.get("comment", None) + + self.check_permissions(request, 'edit_comment', history_entry) + + if history_entry is None: + return response.NotFound() + + if comment is None: + return response.BadRequest({"error": _("comment is required")}) + + if history_entry.delete_comment_date or history_entry.delete_comment_user: + return response.BadRequest({"error": _("deleted comments can't be edited")}) + + # comment_versions can be None if there are no historic versions of the comment + comment_versions = history_entry.comment_versions or [] + comment_versions.append({ + "date": history_entry.created_at, + "comment": history_entry.comment, + "comment_html": history_entry.comment_html, + "user": { + "id": request.user.pk, + } + }) + + history_entry.edit_comment_date = timezone.now() + history_entry.comment = comment + history_entry.comment_html = mdrender(obj.project, comment) + history_entry.comment_versions = comment_versions + history_entry.save() + return response.Ok() + @detail_route(methods=['post']) def delete_comment(self, request, pk): obj = self.get_object() - comment_id = request.QUERY_PARAMS.get('id', None) - comment = services.get_history_queryset_by_model_instance(obj).filter(id=comment_id).first() + history_entry_id = request.QUERY_PARAMS.get('id', None) + history_entry = services.get_history_queryset_by_model_instance(obj).filter(id=history_entry_id).first() - self.check_permissions(request, 'delete_comment', comment) + self.check_permissions(request, 'delete_comment', history_entry) - if comment is None: + if history_entry is None: return response.NotFound() - if comment.delete_comment_date or comment.delete_comment_user: + if history_entry.delete_comment_date or history_entry.delete_comment_user: return response.BadRequest({"error": _("Comment already deleted")}) - comment.delete_comment_date = timezone.now() - comment.delete_comment_user = {"pk": request.user.pk, "name": request.user.get_full_name()} - comment.save() + history_entry.delete_comment_date = timezone.now() + history_entry.delete_comment_user = {"pk": request.user.pk, "name": request.user.get_full_name()} + history_entry.save() return response.Ok() @detail_route(methods=['post']) def undelete_comment(self, request, pk): obj = self.get_object() - comment_id = request.QUERY_PARAMS.get('id', None) - comment = services.get_history_queryset_by_model_instance(obj).filter(id=comment_id).first() + history_entry_id = request.QUERY_PARAMS.get('id', None) + history_entry = services.get_history_queryset_by_model_instance(obj).filter(id=history_entry_id).first() - self.check_permissions(request, 'undelete_comment', comment) + self.check_permissions(request, 'undelete_comment', history_entry) - if comment is None: + if history_entry is None: return response.NotFound() - if not comment.delete_comment_date and not comment.delete_comment_user: + if not history_entry.delete_comment_date and not history_entry.delete_comment_user: return response.BadRequest({"error": _("Comment not deleted")}) - comment.delete_comment_date = None - comment.delete_comment_user = None - comment.save() + history_entry.delete_comment_date = None + history_entry.delete_comment_user = None + history_entry.save() return response.Ok() # Just for restframework! Because it raises diff --git a/taiga/projects/history/migrations/0009_auto_20160512_1110.py b/taiga/projects/history/migrations/0009_auto_20160512_1110.py new file mode 100644 index 00000000..0cf39023 --- /dev/null +++ b/taiga/projects/history/migrations/0009_auto_20160512_1110.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.2 on 2016-05-12 11:10 +from __future__ import unicode_literals + +from django.db import migrations, models +import django_pgjson.fields + + +class Migration(migrations.Migration): + + dependencies = [ + ('history', '0008_auto_20150508_1028'), + ] + + operations = [ + migrations.AddField( + model_name='historyentry', + name='comment_versions', + field=django_pgjson.fields.JsonField(blank=True, default=None, null=True), + ), + migrations.AddField( + model_name='historyentry', + name='edit_comment_date', + field=models.DateTimeField(blank=True, default=None, null=True), + ), + ] diff --git a/taiga/projects/history/models.py b/taiga/projects/history/models.py index e947c6fe..39012f60 100644 --- a/taiga/projects/history/models.py +++ b/taiga/projects/history/models.py @@ -67,6 +67,10 @@ class HistoryEntry(models.Model): delete_comment_date = models.DateTimeField(null=True, blank=True, default=None) delete_comment_user = JsonField(null=True, blank=True, default=None) + # Historic version of comments + comment_versions = JsonField(null=True, blank=True, default=None) + edit_comment_date = models.DateTimeField(null=True, blank=True, default=None) + # Flag for mark some history entries as # hidden. Hidden history entries are important # for save but not important to preview. @@ -111,6 +115,20 @@ class HistoryEntry(models.Model): self._owner = owner self._prefetched_owner = True + def attach_user_info_to_comment_versions(self): + if not self.comment_versions: + return + + from taiga.users.serializers import UserSerializer + + user_ids = [v["user"]["id"] for v in self.comment_versions if "user" in v and "id" in v["user"]] + users_by_id = {u.id: u for u in get_user_model().objects.filter(id__in=user_ids)} + + for version in self.comment_versions: + user = users_by_id.get(version["user"]["id"], None) + if user: + version["user"] = UserSerializer(user).data + @cached_property def values_diff(self): result = {} diff --git a/taiga/projects/history/permissions.py b/taiga/projects/history/permissions.py index 015ac22c..fdce68cd 100644 --- a/taiga/projects/history/permissions.py +++ b/taiga/projects/history/permissions.py @@ -33,32 +33,41 @@ class IsCommentOwner(PermissionComponent): return obj.user and obj.user.get("pk", "not-pk") == request.user.pk -class IsCommentProjectOwner(PermissionComponent): +class IsCommentProjectAdmin(PermissionComponent): def check_permissions(self, request, view, obj=None): model = get_model_from_key(obj.key) pk = get_pk_from_key(obj.key) project = model.objects.get(pk=pk) return is_project_admin(request.user, project) + class UserStoryHistoryPermission(TaigaResourcePermission): retrieve_perms = HasProjectPerm('view_project') - delete_comment_perms = IsCommentProjectOwner() | IsCommentOwner() - undelete_comment_perms = IsCommentProjectOwner() | IsCommentDeleter() + edit_comment_perms = IsCommentProjectAdmin() | IsCommentOwner() + delete_comment_perms = IsCommentProjectAdmin() | IsCommentOwner() + undelete_comment_perms = IsCommentProjectAdmin() | IsCommentDeleter() + comment_versions_perms = IsCommentProjectAdmin() | IsCommentOwner() class TaskHistoryPermission(TaigaResourcePermission): retrieve_perms = HasProjectPerm('view_project') - delete_comment_perms = IsCommentProjectOwner() | IsCommentOwner() - undelete_comment_perms = IsCommentProjectOwner() | IsCommentDeleter() + edit_comment_perms = IsCommentProjectAdmin() | IsCommentOwner() + delete_comment_perms = IsCommentProjectAdmin() | IsCommentOwner() + undelete_comment_perms = IsCommentProjectAdmin() | IsCommentDeleter() + comment_versions_perms = IsCommentProjectAdmin() | IsCommentOwner() class IssueHistoryPermission(TaigaResourcePermission): retrieve_perms = HasProjectPerm('view_project') - delete_comment_perms = IsCommentProjectOwner() | IsCommentOwner() - undelete_comment_perms = IsCommentProjectOwner() | IsCommentDeleter() + edit_comment_perms = IsCommentProjectAdmin() | IsCommentOwner() + delete_comment_perms = IsCommentProjectAdmin() | IsCommentOwner() + undelete_comment_perms = IsCommentProjectAdmin() | IsCommentDeleter() + comment_versions_perms = IsCommentProjectAdmin() | IsCommentOwner() class WikiHistoryPermission(TaigaResourcePermission): retrieve_perms = HasProjectPerm('view_project') - delete_comment_perms = IsCommentProjectOwner() | IsCommentOwner() - undelete_comment_perms = IsCommentProjectOwner() | IsCommentDeleter() + edit_comment_perms = IsCommentProjectAdmin() | IsCommentOwner() + delete_comment_perms = IsCommentProjectAdmin() | IsCommentOwner() + undelete_comment_perms = IsCommentProjectAdmin() | IsCommentDeleter() + comment_versions_perms = IsCommentProjectAdmin() | IsCommentOwner() diff --git a/taiga/projects/history/serializers.py b/taiga/projects/history/serializers.py index 72b3c763..f231f29c 100644 --- a/taiga/projects/history/serializers.py +++ b/taiga/projects/history/serializers.py @@ -33,9 +33,11 @@ class HistoryEntrySerializer(serializers.ModelSerializer): values_diff = I18NJsonField(i18n_fields=HISTORY_ENTRY_I18N_FIELDS) user = serializers.SerializerMethodField("get_user") delete_comment_user = JsonField() + comment_versions = JsonField() class Meta: model = models.HistoryEntry + exclude = ("comment_versions",) def get_user(self, entry): user = {"pk": None, "username": None, "name": None, "photo": None, "is_active": False} diff --git a/taiga/projects/history/services.py b/taiga/projects/history/services.py index 22839ec8..61004471 100644 --- a/taiga/projects/history/services.py +++ b/taiga/projects/history/services.py @@ -91,6 +91,20 @@ def get_pk_from_key(key:str) -> object: return pk +def get_instance_from_key(key:str) -> object: + """ + Get instance from key + """ + model = get_model_from_key(key) + pk = get_pk_from_key(key) + try: + obj = model.objects.get(pk=pk) + return obj + except model.DoesNotExist: + # Catch simultaneous DELETE request + return None + + def register_values_implementation(typename:str, fn=None): """ Register values implementation for specified typename. diff --git a/taiga/timeline/signals.py b/taiga/timeline/signals.py index eda08031..c0f1dffa 100644 --- a/taiga/timeline/signals.py +++ b/taiga/timeline/signals.py @@ -44,7 +44,6 @@ def _clean_description_fields(values_diff): def on_new_history_entry(sender, instance, created, **kwargs): - if instance._importing: return @@ -81,6 +80,10 @@ def on_new_history_entry(sender, instance, created, **kwargs): if instance.delete_comment_date: extra_data["comment_deleted"] = True + # Detect edited comment + if instance.comment_versions is not None and len(instance.comment_versions)>0: + extra_data["comment_edited"] = True + created_datetime = instance.created_at _push_to_timelines(project, user, obj, event_type, created_datetime, extra_data=extra_data) diff --git a/tests/integration/resources_permissions/test_history_resources.py b/tests/integration/resources_permissions/test_history_resources.py index b0991080..be17510c 100644 --- a/tests/integration/resources_permissions/test_history_resources.py +++ b/tests/integration/resources_permissions/test_history_resources.py @@ -1,6 +1,11 @@ from django.core.urlresolvers import reverse +from django.utils import timezone +from taiga.base.utils import json from taiga.permissions.permissions import MEMBERS_PERMISSIONS, ANON_PERMISSIONS, USER_PERMISSIONS +from taiga.projects.history.models import HistoryEntry +from taiga.projects.history.choices import HistoryType +from taiga.projects.history.services import make_key_from_model_object from tests import factories as f from tests.utils import helper_test_http_method, disconnect_signals, reconnect_signals @@ -21,11 +26,11 @@ def teardown_module(module): def data(): m = type("Models", (object,), {}) - m.registered_user = f.UserFactory.create() - m.project_member_with_perms = f.UserFactory.create() - m.project_member_without_perms = f.UserFactory.create() - m.project_owner = f.UserFactory.create() - m.other_user = f.UserFactory.create() + m.registered_user = f.UserFactory.create(full_name="registered_user") + m.project_member_with_perms = f.UserFactory.create(full_name="project_member_with_perms") + m.project_member_without_perms = f.UserFactory.create(full_name="project_member_without_perms") + m.project_owner = f.UserFactory.create(full_name="project_owner") + m.other_user = f.UserFactory.create(full_name="other_user") m.public_project = f.ProjectFactory(is_private=False, anon_permissions=list(map(lambda x: x[0], ANON_PERMISSIONS)), @@ -76,39 +81,33 @@ def data(): return m +######################################################### +## User stories +######################################################### + + @pytest.fixture def data_us(data): m = type("Models", (object,), {}) m.public_user_story = f.UserStoryFactory(project=data.public_project, ref=1) + m.public_history_entry = f.HistoryEntryFactory.create(type=HistoryType.change, + comment="testing public", + key=make_key_from_model_object(m.public_user_story), + diff={}, + user={"pk": data.project_member_with_perms.pk}) + m.private_user_story1 = f.UserStoryFactory(project=data.private_project1, ref=5) + m.private_history_entry1 = f.HistoryEntryFactory.create(type=HistoryType.change, + comment="testing 1", + key=make_key_from_model_object(m.private_user_story1), + diff={}, + user={"pk": data.project_member_with_perms.pk}) m.private_user_story2 = f.UserStoryFactory(project=data.private_project2, ref=9) - return m - - -@pytest.fixture -def data_task(data): - m = type("Models", (object,), {}) - m.public_task = f.TaskFactory(project=data.public_project, ref=2) - m.private_task1 = f.TaskFactory(project=data.private_project1, ref=6) - m.private_task2 = f.TaskFactory(project=data.private_project2, ref=10) - return m - - -@pytest.fixture -def data_issue(data): - m = type("Models", (object,), {}) - m.public_issue = f.IssueFactory(project=data.public_project, ref=3) - m.private_issue1 = f.IssueFactory(project=data.private_project1, ref=7) - m.private_issue2 = f.IssueFactory(project=data.private_project2, ref=11) - return m - - -@pytest.fixture -def data_wiki(data): - m = type("Models", (object,), {}) - m.public_wiki = f.WikiPageFactory(project=data.public_project, slug=4) - m.private_wiki1 = f.WikiPageFactory(project=data.private_project1, slug=8) - m.private_wiki2 = f.WikiPageFactory(project=data.private_project2, slug=12) + m.private_history_entry2 = f.HistoryEntryFactory.create(type=HistoryType.change, + comment="testing 2", + key=make_key_from_model_object(m.private_user_story2), + diff={}, + user={"pk": data.project_member_with_perms.pk}) return m @@ -133,6 +132,222 @@ def test_user_story_history_retrieve(client, data, data_us): assert results == [401, 403, 403, 200, 200] +def test_user_story_action_edit_comment(client, data, data_us): + public_url = "{}?id={}".format( + reverse('userstory-history-edit-comment', kwargs={"pk": data_us.public_user_story.pk}), + data_us.public_history_entry.id + ) + private_url1 = "{}?id={}".format( + reverse('userstory-history-edit-comment', kwargs={"pk": data_us.private_user_story1.pk}), + data_us.private_history_entry1.id + ) + private_url2 = "{}?id={}".format( + reverse('userstory-history-edit-comment', kwargs={"pk": data_us.private_user_story2.pk}), + data_us.private_history_entry2.id + ) + + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + data.project_owner + ] + + data = json.dumps({"comment": "testing update comment"}) + + results = helper_test_http_method(client, 'post', public_url, data, users) + assert results == [401, 403, 403, 200, 200] + results = helper_test_http_method(client, 'post', private_url1, data, users) + assert results == [401, 403, 403, 200, 200] + results = helper_test_http_method(client, 'post', private_url2, data, users) + assert results == [401, 403, 403, 200, 200] + + +def test_user_story_action_delete_comment(client, data, data_us): + public_url = "{}?id={}".format( + reverse('userstory-history-delete-comment', kwargs={"pk": data_us.public_user_story.pk}), + data_us.public_history_entry.id + ) + private_url1 = "{}?id={}".format( + reverse('userstory-history-delete-comment', kwargs={"pk": data_us.private_user_story1.pk}), + data_us.private_history_entry1.id + ) + private_url2 = "{}?id={}".format( + reverse('userstory-history-delete-comment', kwargs={"pk": data_us.private_user_story2.pk}), + data_us.private_history_entry2.id + ) + + users_and_statuses = [ + (None, 401), + (data.registered_user, 403), + (data.project_member_without_perms, 403), + (data.project_member_with_perms, 200), + (data.project_owner, 200), + ] + + for user, status_code in users_and_statuses: + data_us.public_history_entry.delete_comment_date = None + data_us.public_history_entry.delete_comment_user = None + data_us.public_history_entry.save() + + if user: + client.login(user) + else: + client.logout() + response = client.json.post(public_url) + error_mesage = "{} != {} for {}".format(response.status_code, status_code, user) + assert response.status_code == status_code, error_mesage + + for user, status_code in users_and_statuses: + data_us.private_history_entry1.delete_comment_date = None + data_us.private_history_entry1.delete_comment_user = None + data_us.private_history_entry1.save() + + if user: + client.login(user) + else: + client.logout() + response = client.json.post(private_url1) + error_mesage = "{} != {} for {}".format(response.status_code, status_code, user) + assert response.status_code == status_code, error_mesage + + for user, status_code in users_and_statuses: + data_us.private_history_entry2.delete_comment_date = None + data_us.private_history_entry2.delete_comment_user = None + data_us.private_history_entry2.save() + + if user: + client.login(user) + else: + client.logout() + response = client.json.post(private_url2) + error_mesage = "{} != {} for {}".format(response.status_code, status_code, user) + assert response.status_code == status_code, error_mesage + + +def test_user_story_action_undelete_comment(client, data, data_us): + public_url = "{}?id={}".format( + reverse('userstory-history-undelete-comment', kwargs={"pk": data_us.public_user_story.pk}), + data_us.public_history_entry.id + ) + private_url1 = "{}?id={}".format( + reverse('userstory-history-undelete-comment', kwargs={"pk": data_us.private_user_story1.pk}), + data_us.private_history_entry1.id + ) + private_url2 = "{}?id={}".format( + reverse('userstory-history-undelete-comment', kwargs={"pk": data_us.private_user_story2.pk}), + data_us.private_history_entry2.id + ) + + users_and_statuses = [ + (None, 401), + (data.registered_user, 403), + (data.project_member_without_perms, 403), + (data.project_member_with_perms, 200), + (data.project_owner, 200), + ] + + for user, status_code in users_and_statuses: + data_us.public_history_entry.delete_comment_date = timezone.now() + data_us.public_history_entry.delete_comment_user = {"pk": data.project_member_with_perms.pk} + data_us.public_history_entry.save() + + if user: + client.login(user) + else: + client.logout() + response = client.json.post(public_url) + error_mesage = "{} != {} for {}".format(response.status_code, status_code, user) + assert response.status_code == status_code, error_mesage + + for user, status_code in users_and_statuses: + data_us.private_history_entry1.delete_comment_date = timezone.now() + data_us.private_history_entry1.delete_comment_user = {"pk": data.project_member_with_perms.pk} + data_us.private_history_entry1.save() + + if user: + client.login(user) + else: + client.logout() + response = client.json.post(private_url1) + error_mesage = "{} != {} for {}".format(response.status_code, status_code, user) + assert response.status_code == status_code, error_mesage + + for user, status_code in users_and_statuses: + data_us.private_history_entry2.delete_comment_date = timezone.now() + data_us.private_history_entry2.delete_comment_user = {"pk": data.project_member_with_perms.pk} + data_us.private_history_entry2.save() + + if user: + client.login(user) + else: + client.logout() + response = client.json.post(private_url2) + error_mesage = "{} != {} for {}".format(response.status_code, status_code, user) + assert response.status_code == status_code, error_mesage + + +def test_user_story_action_comment_versions(client, data, data_us): + public_url = "{}?id={}".format( + reverse('userstory-history-comment-versions', kwargs={"pk": data_us.public_user_story.pk}), + data_us.public_history_entry.id + ) + private_url1 = "{}?id={}".format( + reverse('userstory-history-comment-versions', kwargs={"pk": data_us.private_user_story1.pk}), + data_us.private_history_entry1.id + ) + private_url2 = "{}?id={}".format( + reverse('userstory-history-comment-versions', kwargs={"pk": data_us.private_user_story2.pk}), + data_us.private_history_entry2.id + ) + + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + data.project_owner, + ] + + results = helper_test_http_method(client, 'get', public_url, None, users) + assert results == [401, 403, 403, 200, 200] + results = helper_test_http_method(client, 'get', private_url1, None, users) + assert results == [401, 403, 403, 200, 200] + results = helper_test_http_method(client, 'get', private_url2, None, users) + assert results == [401, 403, 403, 200, 200] + + +######################################################### +## Tasks +######################################################### + + +@pytest.fixture +def data_task(data): + m = type("Models", (object,), {}) + m.public_task = f.TaskFactory(project=data.public_project, ref=2) + m.public_history_entry = f.HistoryEntryFactory.create(type=HistoryType.change, + comment="testing public", + key=make_key_from_model_object(m.public_task), + diff={}, + user={"pk": data.project_member_with_perms.pk}) + + m.private_task1 = f.TaskFactory(project=data.private_project1, ref=6) + m.private_history_entry1 = f.HistoryEntryFactory.create(type=HistoryType.change, + comment="testing 1", + key=make_key_from_model_object(m.private_task1), + diff={}, + user={"pk": data.project_member_with_perms.pk}) + m.private_task2 = f.TaskFactory(project=data.private_project2, ref=10) + m.private_history_entry2 = f.HistoryEntryFactory.create(type=HistoryType.change, + comment="testing 2", + key=make_key_from_model_object(m.private_task2), + diff={}, + user={"pk": data.project_member_with_perms.pk}) + return m + + def test_task_history_retrieve(client, data, data_task): public_url = reverse('task-history-detail', kwargs={"pk": data_task.public_task.pk}) private_url1 = reverse('task-history-detail', kwargs={"pk": data_task.private_task1.pk}) @@ -154,6 +369,222 @@ def test_task_history_retrieve(client, data, data_task): assert results == [401, 403, 403, 200, 200] +def test_task_action_edit_comment(client, data, data_task): + public_url = "{}?id={}".format( + reverse('task-history-edit-comment', kwargs={"pk": data_task.public_task.pk}), + data_task.public_history_entry.id + ) + private_url1 = "{}?id={}".format( + reverse('task-history-edit-comment', kwargs={"pk": data_task.private_task1.pk}), + data_task.private_history_entry1.id + ) + private_url2 = "{}?id={}".format( + reverse('task-history-edit-comment', kwargs={"pk": data_task.private_task2.pk}), + data_task.private_history_entry2.id + ) + + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + data.project_owner + ] + + data = json.dumps({"comment": "testing update comment"}) + + results = helper_test_http_method(client, 'post', public_url, data, users) + assert results == [401, 403, 403, 200, 200] + results = helper_test_http_method(client, 'post', private_url1, data, users) + assert results == [401, 403, 403, 200, 200] + results = helper_test_http_method(client, 'post', private_url2, data, users) + assert results == [401, 403, 403, 200, 200] + + +def test_task_action_delete_comment(client, data, data_task): + public_url = "{}?id={}".format( + reverse('task-history-delete-comment', kwargs={"pk": data_task.public_task.pk}), + data_task.public_history_entry.id + ) + private_url1 = "{}?id={}".format( + reverse('task-history-delete-comment', kwargs={"pk": data_task.private_task1.pk}), + data_task.private_history_entry1.id + ) + private_url2 = "{}?id={}".format( + reverse('task-history-delete-comment', kwargs={"pk": data_task.private_task2.pk}), + data_task.private_history_entry2.id + ) + + users_and_statuses = [ + (None, 401), + (data.registered_user, 403), + (data.project_member_without_perms, 403), + (data.project_member_with_perms, 200), + (data.project_owner, 200), + ] + + for user, status_code in users_and_statuses: + data_task.public_history_entry.delete_comment_date = None + data_task.public_history_entry.delete_comment_user = None + data_task.public_history_entry.save() + + if user: + client.login(user) + else: + client.logout() + response = client.json.post(public_url) + error_mesage = "{} != {} for {}".format(response.status_code, status_code, user) + assert response.status_code == status_code, error_mesage + + for user, status_code in users_and_statuses: + data_task.private_history_entry1.delete_comment_date = None + data_task.private_history_entry1.delete_comment_user = None + data_task.private_history_entry1.save() + + if user: + client.login(user) + else: + client.logout() + response = client.json.post(private_url1) + error_mesage = "{} != {} for {}".format(response.status_code, status_code, user) + assert response.status_code == status_code, error_mesage + + for user, status_code in users_and_statuses: + data_task.private_history_entry2.delete_comment_date = None + data_task.private_history_entry2.delete_comment_user = None + data_task.private_history_entry2.save() + + if user: + client.login(user) + else: + client.logout() + response = client.json.post(private_url2) + error_mesage = "{} != {} for {}".format(response.status_code, status_code, user) + assert response.status_code == status_code, error_mesage + + +def test_task_action_undelete_comment(client, data, data_task): + public_url = "{}?id={}".format( + reverse('task-history-undelete-comment', kwargs={"pk": data_task.public_task.pk}), + data_task.public_history_entry.id + ) + private_url1 = "{}?id={}".format( + reverse('task-history-undelete-comment', kwargs={"pk": data_task.private_task1.pk}), + data_task.private_history_entry1.id + ) + private_url2 = "{}?id={}".format( + reverse('task-history-undelete-comment', kwargs={"pk": data_task.private_task2.pk}), + data_task.private_history_entry2.id + ) + + users_and_statuses = [ + (None, 401), + (data.registered_user, 403), + (data.project_member_without_perms, 403), + (data.project_member_with_perms, 200), + (data.project_owner, 200), + ] + + for user, status_code in users_and_statuses: + data_task.public_history_entry.delete_comment_date = timezone.now() + data_task.public_history_entry.delete_comment_user = {"pk": data.project_member_with_perms.pk} + data_task.public_history_entry.save() + + if user: + client.login(user) + else: + client.logout() + response = client.json.post(public_url) + error_mesage = "{} != {} for {}".format(response.status_code, status_code, user) + assert response.status_code == status_code, error_mesage + + for user, status_code in users_and_statuses: + data_task.private_history_entry1.delete_comment_date = timezone.now() + data_task.private_history_entry1.delete_comment_user = {"pk": data.project_member_with_perms.pk} + data_task.private_history_entry1.save() + + if user: + client.login(user) + else: + client.logout() + response = client.json.post(private_url1) + error_mesage = "{} != {} for {}".format(response.status_code, status_code, user) + assert response.status_code == status_code, error_mesage + + for user, status_code in users_and_statuses: + data_task.private_history_entry2.delete_comment_date = timezone.now() + data_task.private_history_entry2.delete_comment_user = {"pk": data.project_member_with_perms.pk} + data_task.private_history_entry2.save() + + if user: + client.login(user) + else: + client.logout() + response = client.json.post(private_url2) + error_mesage = "{} != {} for {}".format(response.status_code, status_code, user) + assert response.status_code == status_code, error_mesage + + +def test_task_action_comment_versions(client, data, data_task): + public_url = "{}?id={}".format( + reverse('task-history-comment-versions', kwargs={"pk": data_task.public_task.pk}), + data_task.public_history_entry.id + ) + private_url1 = "{}?id={}".format( + reverse('task-history-comment-versions', kwargs={"pk": data_task.private_task1.pk}), + data_task.private_history_entry1.id + ) + private_url2 = "{}?id={}".format( + reverse('task-history-comment-versions', kwargs={"pk": data_task.private_task2.pk}), + data_task.private_history_entry2.id + ) + + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + data.project_owner, + ] + + results = helper_test_http_method(client, 'get', public_url, None, users) + assert results == [401, 403, 403, 200, 200] + results = helper_test_http_method(client, 'get', private_url1, None, users) + assert results == [401, 403, 403, 200, 200] + results = helper_test_http_method(client, 'get', private_url2, None, users) + assert results == [401, 403, 403, 200, 200] + + +######################################################### +## Issues +######################################################### + + +@pytest.fixture +def data_issue(data): + m = type("Models", (object,), {}) + m.public_issue = f.IssueFactory(project=data.public_project, ref=3) + m.public_history_entry = f.HistoryEntryFactory.create(type=HistoryType.change, + comment="testing public", + key=make_key_from_model_object(m.public_issue), + diff={}, + user={"pk": data.project_member_with_perms.pk}) + + m.private_issue1 = f.IssueFactory(project=data.private_project1, ref=7) + m.private_history_entry1 = f.HistoryEntryFactory.create(type=HistoryType.change, + comment="testing 1", + key=make_key_from_model_object(m.private_issue1), + diff={}, + user={"pk": data.project_member_with_perms.pk}) + m.private_issue2 = f.IssueFactory(project=data.private_project2, ref=11) + m.private_history_entry2 = f.HistoryEntryFactory.create(type=HistoryType.change, + comment="testing 2", + key=make_key_from_model_object(m.private_issue2), + diff={}, + user={"pk": data.project_member_with_perms.pk}) + return m + + def test_issue_history_retrieve(client, data, data_issue): public_url = reverse('issue-history-detail', kwargs={"pk": data_issue.public_issue.pk}) private_url1 = reverse('issue-history-detail', kwargs={"pk": data_issue.private_issue1.pk}) @@ -175,6 +606,222 @@ def test_issue_history_retrieve(client, data, data_issue): assert results == [401, 403, 403, 200, 200] +def test_issue_action_edit_comment(client, data, data_issue): + public_url = "{}?id={}".format( + reverse('issue-history-edit-comment', kwargs={"pk": data_issue.public_issue.pk}), + data_issue.public_history_entry.id + ) + private_url1 = "{}?id={}".format( + reverse('issue-history-edit-comment', kwargs={"pk": data_issue.private_issue1.pk}), + data_issue.private_history_entry1.id + ) + private_url2 = "{}?id={}".format( + reverse('issue-history-edit-comment', kwargs={"pk": data_issue.private_issue2.pk}), + data_issue.private_history_entry2.id + ) + + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + data.project_owner + ] + + data = json.dumps({"comment": "testing update comment"}) + + results = helper_test_http_method(client, 'post', public_url, data, users) + assert results == [401, 403, 403, 200, 200] + results = helper_test_http_method(client, 'post', private_url1, data, users) + assert results == [401, 403, 403, 200, 200] + results = helper_test_http_method(client, 'post', private_url2, data, users) + assert results == [401, 403, 403, 200, 200] + + +def test_issue_action_delete_comment(client, data, data_issue): + public_url = "{}?id={}".format( + reverse('issue-history-delete-comment', kwargs={"pk": data_issue.public_issue.pk}), + data_issue.public_history_entry.id + ) + private_url1 = "{}?id={}".format( + reverse('issue-history-delete-comment', kwargs={"pk": data_issue.private_issue1.pk}), + data_issue.private_history_entry1.id + ) + private_url2 = "{}?id={}".format( + reverse('issue-history-delete-comment', kwargs={"pk": data_issue.private_issue2.pk}), + data_issue.private_history_entry2.id + ) + + users_and_statuses = [ + (None, 401), + (data.registered_user, 403), + (data.project_member_without_perms, 403), + (data.project_member_with_perms, 200), + (data.project_owner, 200), + ] + + for user, status_code in users_and_statuses: + data_issue.public_history_entry.delete_comment_date = None + data_issue.public_history_entry.delete_comment_user = None + data_issue.public_history_entry.save() + + if user: + client.login(user) + else: + client.logout() + response = client.json.post(public_url) + error_mesage = "{} != {} for {}".format(response.status_code, status_code, user) + assert response.status_code == status_code, error_mesage + + for user, status_code in users_and_statuses: + data_issue.private_history_entry1.delete_comment_date = None + data_issue.private_history_entry1.delete_comment_user = None + data_issue.private_history_entry1.save() + + if user: + client.login(user) + else: + client.logout() + response = client.json.post(private_url1) + error_mesage = "{} != {} for {}".format(response.status_code, status_code, user) + assert response.status_code == status_code, error_mesage + + for user, status_code in users_and_statuses: + data_issue.private_history_entry2.delete_comment_date = None + data_issue.private_history_entry2.delete_comment_user = None + data_issue.private_history_entry2.save() + + if user: + client.login(user) + else: + client.logout() + response = client.json.post(private_url2) + error_mesage = "{} != {} for {}".format(response.status_code, status_code, user) + assert response.status_code == status_code, error_mesage + + +def test_issue_action_undelete_comment(client, data, data_issue): + public_url = "{}?id={}".format( + reverse('issue-history-undelete-comment', kwargs={"pk": data_issue.public_issue.pk}), + data_issue.public_history_entry.id + ) + private_url1 = "{}?id={}".format( + reverse('issue-history-undelete-comment', kwargs={"pk": data_issue.private_issue1.pk}), + data_issue.private_history_entry1.id + ) + private_url2 = "{}?id={}".format( + reverse('issue-history-undelete-comment', kwargs={"pk": data_issue.private_issue2.pk}), + data_issue.private_history_entry2.id + ) + + users_and_statuses = [ + (None, 401), + (data.registered_user, 403), + (data.project_member_without_perms, 403), + (data.project_member_with_perms, 200), + (data.project_owner, 200), + ] + + for user, status_code in users_and_statuses: + data_issue.public_history_entry.delete_comment_date = timezone.now() + data_issue.public_history_entry.delete_comment_user = {"pk": data.project_member_with_perms.pk} + data_issue.public_history_entry.save() + + if user: + client.login(user) + else: + client.logout() + response = client.json.post(public_url) + error_mesage = "{} != {} for {}".format(response.status_code, status_code, user) + assert response.status_code == status_code, error_mesage + + for user, status_code in users_and_statuses: + data_issue.private_history_entry1.delete_comment_date = timezone.now() + data_issue.private_history_entry1.delete_comment_user = {"pk": data.project_member_with_perms.pk} + data_issue.private_history_entry1.save() + + if user: + client.login(user) + else: + client.logout() + response = client.json.post(private_url1) + error_mesage = "{} != {} for {}".format(response.status_code, status_code, user) + assert response.status_code == status_code, error_mesage + + for user, status_code in users_and_statuses: + data_issue.private_history_entry2.delete_comment_date = timezone.now() + data_issue.private_history_entry2.delete_comment_user = {"pk": data.project_member_with_perms.pk} + data_issue.private_history_entry2.save() + + if user: + client.login(user) + else: + client.logout() + response = client.json.post(private_url2) + error_mesage = "{} != {} for {}".format(response.status_code, status_code, user) + assert response.status_code == status_code, error_mesage + + +def test_issue_action_comment_versions(client, data, data_issue): + public_url = "{}?id={}".format( + reverse('issue-history-comment-versions', kwargs={"pk": data_issue.public_issue.pk}), + data_issue.public_history_entry.id + ) + private_url1 = "{}?id={}".format( + reverse('issue-history-comment-versions', kwargs={"pk": data_issue.private_issue1.pk}), + data_issue.private_history_entry1.id + ) + private_url2 = "{}?id={}".format( + reverse('issue-history-comment-versions', kwargs={"pk": data_issue.private_issue2.pk}), + data_issue.private_history_entry2.id + ) + + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + data.project_owner, + ] + + results = helper_test_http_method(client, 'get', public_url, None, users) + assert results == [401, 403, 403, 200, 200] + results = helper_test_http_method(client, 'get', private_url1, None, users) + assert results == [401, 403, 403, 200, 200] + results = helper_test_http_method(client, 'get', private_url2, None, users) + assert results == [401, 403, 403, 200, 200] + + +######################################################### +## Wiki pages +######################################################### + + +@pytest.fixture +def data_wiki(data): + m = type("Models", (object,), {}) + m.public_wiki = f.WikiPageFactory(project=data.public_project, slug=4) + m.public_history_entry = f.HistoryEntryFactory.create(type=HistoryType.change, + comment="testing public", + key=make_key_from_model_object(m.public_wiki), + diff={}, + user={"pk": data.project_member_with_perms.pk}) + + m.private_wiki1 = f.WikiPageFactory(project=data.private_project1, slug=8) + m.private_history_entry1 = f.HistoryEntryFactory.create(type=HistoryType.change, + comment="testing 1", + key=make_key_from_model_object(m.private_wiki1), + diff={}, + user={"pk": data.project_member_with_perms.pk}) + m.private_wiki2 = f.WikiPageFactory(project=data.private_project2, slug=12) + m.private_history_entry2 = f.HistoryEntryFactory.create(type=HistoryType.change, + comment="testing 2", + key=make_key_from_model_object(m.private_wiki2), + diff={}, + user={"pk": data.project_member_with_perms.pk}) + return m + + def test_wiki_history_retrieve(client, data, data_wiki): public_url = reverse('wiki-history-detail', kwargs={"pk": data_wiki.public_wiki.pk}) private_url1 = reverse('wiki-history-detail', kwargs={"pk": data_wiki.private_wiki1.pk}) @@ -194,3 +841,189 @@ def test_wiki_history_retrieve(client, data, data_wiki): assert results == [200, 200, 200, 200, 200] results = helper_test_http_method(client, 'get', private_url2, None, users) assert results == [401, 403, 403, 200, 200] + + +def test_wiki_action_edit_comment(client, data, data_wiki): + public_url = "{}?id={}".format( + reverse('wiki-history-edit-comment', kwargs={"pk": data_wiki.public_wiki.pk}), + data_wiki.public_history_entry.id + ) + private_url1 = "{}?id={}".format( + reverse('wiki-history-edit-comment', kwargs={"pk": data_wiki.private_wiki1.pk}), + data_wiki.private_history_entry1.id + ) + private_url2 = "{}?id={}".format( + reverse('wiki-history-edit-comment', kwargs={"pk": data_wiki.private_wiki2.pk}), + data_wiki.private_history_entry2.id + ) + + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + data.project_owner + ] + + data = json.dumps({"comment": "testing update comment"}) + + results = helper_test_http_method(client, 'post', public_url, data, users) + assert results == [401, 403, 403, 200, 200] + results = helper_test_http_method(client, 'post', private_url1, data, users) + assert results == [401, 403, 403, 200, 200] + results = helper_test_http_method(client, 'post', private_url2, data, users) + assert results == [401, 403, 403, 200, 200] + + +def test_wiki_action_delete_comment(client, data, data_wiki): + public_url = "{}?id={}".format( + reverse('wiki-history-delete-comment', kwargs={"pk": data_wiki.public_wiki.pk}), + data_wiki.public_history_entry.id + ) + private_url1 = "{}?id={}".format( + reverse('wiki-history-delete-comment', kwargs={"pk": data_wiki.private_wiki1.pk}), + data_wiki.private_history_entry1.id + ) + private_url2 = "{}?id={}".format( + reverse('wiki-history-delete-comment', kwargs={"pk": data_wiki.private_wiki2.pk}), + data_wiki.private_history_entry2.id + ) + + users_and_statuses = [ + (None, 401), + (data.registered_user, 403), + (data.project_member_without_perms, 403), + (data.project_member_with_perms, 200), + (data.project_owner, 200), + ] + + for user, status_code in users_and_statuses: + data_wiki.public_history_entry.delete_comment_date = None + data_wiki.public_history_entry.delete_comment_user = None + data_wiki.public_history_entry.save() + + if user: + client.login(user) + else: + client.logout() + response = client.json.post(public_url) + error_mesage = "{} != {} for {}".format(response.status_code, status_code, user) + assert response.status_code == status_code, error_mesage + + for user, status_code in users_and_statuses: + data_wiki.private_history_entry1.delete_comment_date = None + data_wiki.private_history_entry1.delete_comment_user = None + data_wiki.private_history_entry1.save() + + if user: + client.login(user) + else: + client.logout() + response = client.json.post(private_url1) + error_mesage = "{} != {} for {}".format(response.status_code, status_code, user) + assert response.status_code == status_code, error_mesage + + for user, status_code in users_and_statuses: + data_wiki.private_history_entry2.delete_comment_date = None + data_wiki.private_history_entry2.delete_comment_user = None + data_wiki.private_history_entry2.save() + + if user: + client.login(user) + else: + client.logout() + response = client.json.post(private_url2) + error_mesage = "{} != {} for {}".format(response.status_code, status_code, user) + assert response.status_code == status_code, error_mesage + + +def test_wiki_action_undelete_comment(client, data, data_wiki): + public_url = "{}?id={}".format( + reverse('wiki-history-undelete-comment', kwargs={"pk": data_wiki.public_wiki.pk}), + data_wiki.public_history_entry.id + ) + private_url1 = "{}?id={}".format( + reverse('wiki-history-undelete-comment', kwargs={"pk": data_wiki.private_wiki1.pk}), + data_wiki.private_history_entry1.id + ) + private_url2 = "{}?id={}".format( + reverse('wiki-history-undelete-comment', kwargs={"pk": data_wiki.private_wiki2.pk}), + data_wiki.private_history_entry2.id + ) + + users_and_statuses = [ + (None, 401), + (data.registered_user, 403), + (data.project_member_without_perms, 403), + (data.project_member_with_perms, 200), + (data.project_owner, 200), + ] + + for user, status_code in users_and_statuses: + data_wiki.public_history_entry.delete_comment_date = timezone.now() + data_wiki.public_history_entry.delete_comment_user = {"pk": data.project_member_with_perms.pk} + data_wiki.public_history_entry.save() + + if user: + client.login(user) + else: + client.logout() + response = client.json.post(public_url) + error_mesage = "{} != {} for {}".format(response.status_code, status_code, user) + assert response.status_code == status_code, error_mesage + + for user, status_code in users_and_statuses: + data_wiki.private_history_entry1.delete_comment_date = timezone.now() + data_wiki.private_history_entry1.delete_comment_user = {"pk": data.project_member_with_perms.pk} + data_wiki.private_history_entry1.save() + + if user: + client.login(user) + else: + client.logout() + response = client.json.post(private_url1) + error_mesage = "{} != {} for {}".format(response.status_code, status_code, user) + assert response.status_code == status_code, error_mesage + + for user, status_code in users_and_statuses: + data_wiki.private_history_entry2.delete_comment_date = timezone.now() + data_wiki.private_history_entry2.delete_comment_user = {"pk": data.project_member_with_perms.pk} + data_wiki.private_history_entry2.save() + + if user: + client.login(user) + else: + client.logout() + response = client.json.post(private_url2) + error_mesage = "{} != {} for {}".format(response.status_code, status_code, user) + assert response.status_code == status_code, error_mesage + + +def test_wiki_action_comment_versions(client, data, data_wiki): + public_url = "{}?id={}".format( + reverse('wiki-history-comment-versions', kwargs={"pk": data_wiki.public_wiki.pk}), + data_wiki.public_history_entry.id + ) + private_url1 = "{}?id={}".format( + reverse('wiki-history-comment-versions', kwargs={"pk": data_wiki.private_wiki1.pk}), + data_wiki.private_history_entry1.id + ) + private_url2 = "{}?id={}".format( + reverse('wiki-history-comment-versions', kwargs={"pk": data_wiki.private_wiki2.pk}), + data_wiki.private_history_entry2.id + ) + + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + data.project_owner, + ] + + results = helper_test_http_method(client, 'get', public_url, None, users) + assert results == [401, 403, 403, 200, 200] + results = helper_test_http_method(client, 'get', private_url1, None, users) + assert results == [401, 403, 403, 200, 200] + results = helper_test_http_method(client, 'get', private_url2, None, users) + assert results == [401, 403, 403, 200, 200] diff --git a/tests/integration/test_history.py b/tests/integration/test_history.py index 01469c0a..5d936c0d 100644 --- a/tests/integration/test_history.py +++ b/tests/integration/test_history.py @@ -17,6 +17,8 @@ # along with this program. If not, see . import pytest +import datetime + from unittest.mock import patch from django.core.urlresolvers import reverse @@ -235,3 +237,84 @@ def test_delete_comment_by_project_owner(client): url = "%s?id=%s" % (url, history_entry.id) response = client.post(url, content_type="application/json") assert 200 == response.status_code, response.status_code + + +def test_edit_comment(client): + project = f.create_project() + us = f.create_userstory(project=project) + f.MembershipFactory.create(project=project, user=project.owner, is_admin=True) + key = make_key_from_model_object(us) + history_entry = f.HistoryEntryFactory.create(type=HistoryType.change, + comment="testing", + key=key, + diff={}, + user={"pk": project.owner.id}) + + history_entry_created_at = history_entry.created_at + assert history_entry.comment_versions == None + assert history_entry.edit_comment_date == None + + client.login(project.owner) + url = reverse("userstory-history-edit-comment", args=(us.id,)) + url = "%s?id=%s" % (url, history_entry.id) + + data = json.dumps({"comment": "testing update comment"}) + response = client.post(url, data, content_type="application/json") + assert 200 == response.status_code, response.status_code + + + history_entry = HistoryEntry.objects.get(id=history_entry.id) + assert len(history_entry.comment_versions) == 1 + assert history_entry.comment == "testing update comment" + assert history_entry.comment_versions[0]["comment"] == "testing" + assert history_entry.edit_comment_date != None + assert history_entry.comment_versions[0]["user"]["id"] == project.owner.id + + +def test_get_comment_versions(client): + project = f.create_project() + us = f.create_userstory(project=project) + f.MembershipFactory.create(project=project, user=project.owner, is_admin=True) + key = make_key_from_model_object(us) + history_entry = f.HistoryEntryFactory.create( + type=HistoryType.change, + comment="testing", + key=key, + diff={}, + user={"pk": project.owner.id}, + edit_comment_date=datetime.datetime.now(), + comment_versions = [{ + "comment_html": "

test

", + "date": "2016-05-09T09:34:27.221Z", + "comment": "test", + "user": { + "id": project.owner.id, + }}]) + + client.login(project.owner) + url = reverse("userstory-history-comment-versions", args=(us.id,)) + url = "%s?id=%s" % (url, history_entry.id) + + response = client.get(url, content_type="application/json") + assert 200 == response.status_code, response.status_code + assert response.data[0]["user"]["username"] == project.owner.username + + +def test_get_comment_versions_from_history_entry_without_comment(client): + project = f.create_project() + us = f.create_userstory(project=project) + f.MembershipFactory.create(project=project, user=project.owner, is_admin=True) + key = make_key_from_model_object(us) + history_entry = f.HistoryEntryFactory.create( + type=HistoryType.change, + key=key, + diff={}, + user={"pk": project.owner.id}) + + client.login(project.owner) + url = reverse("userstory-history-comment-versions", args=(us.id,)) + url = "%s?id=%s" % (url, history_entry.id) + + response = client.get(url, content_type="application/json") + assert 200 == response.status_code, response.status_code + assert response.data == None From 591614e57adc877f93f9b87fced790e25188f3b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Wed, 11 May 2016 20:36:33 +0200 Subject: [PATCH 2/4] Remove USER_PERMISSIONS --- taiga/permissions/permissions.py | 20 ----- taiga/permissions/service.py | 4 +- .../test_attachment_resources.py | 16 ++-- .../test_history_resources.py | 6 +- .../test_issues_custom_attributes_resource.py | 6 +- .../test_issues_resources.py | 38 ++++----- .../test_milestones_resources.py | 6 +- .../test_modules_resources.py | 6 +- .../test_resolver_resources.py | 6 +- .../test_search_resources.py | 6 +- .../test_tasks_custom_attributes_resource.py | 6 +- .../test_tasks_resources.py | 6 +- .../test_timelines_resources.py | 6 +- ..._userstories_custom_attributes_resource.py | 6 +- .../test_userstories_resources.py | 78 +++++++++---------- .../test_wiki_resources.py | 50 ++++++------ tests/integration/test_users.py | 4 +- tests/integration/test_watch_projects.py | 4 +- 18 files changed, 127 insertions(+), 147 deletions(-) diff --git a/taiga/permissions/permissions.py b/taiga/permissions/permissions.py index edd24618..61c0b683 100644 --- a/taiga/permissions/permissions.py +++ b/taiga/permissions/permissions.py @@ -28,26 +28,6 @@ ANON_PERMISSIONS = [ ('view_wiki_links', _('View wiki links')), ] -USER_PERMISSIONS = [ - ('view_project', _('View project')), - ('view_milestones', _('View milestones')), - ('view_us', _('View user stories')), - ('view_issues', _('View issues')), - ('view_tasks', _('View tasks')), - ('view_wiki_pages', _('View wiki pages')), - ('view_wiki_links', _('View wiki links')), - ('request_membership', _('Request membership')), - ('add_us_to_project', _('Add user story to project')), - ('add_comments_to_us', _('Add comments to user stories')), - ('add_comments_to_task', _('Add comments to tasks')), - ('add_issue', _('Add issues')), - ('add_comments_to_issue', _('Add comments to issues')), - ('add_wiki_page', _('Add wiki page')), - ('modify_wiki_page', _('Modify wiki page')), - ('add_wiki_link', _('Add wiki link')), - ('modify_wiki_link', _('Modify wiki link')), -] - MEMBERS_PERMISSIONS = [ ('view_project', _('View project')), # Milestone permissions diff --git a/taiga/permissions/service.py b/taiga/permissions/service.py index 32f79fdc..02922574 100644 --- a/taiga/permissions/service.py +++ b/taiga/permissions/service.py @@ -16,7 +16,7 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . -from .permissions import ADMINS_PERMISSIONS, MEMBERS_PERMISSIONS, ANON_PERMISSIONS, USER_PERMISSIONS +from .permissions import ADMINS_PERMISSIONS, MEMBERS_PERMISSIONS, ANON_PERMISSIONS from django.apps import apps @@ -102,7 +102,7 @@ def get_user_project_permissions(user, project, cache="user"): if user.is_superuser: admins_permissions = list(map(lambda perm: perm[0], ADMINS_PERMISSIONS)) members_permissions = list(map(lambda perm: perm[0], MEMBERS_PERMISSIONS)) - public_permissions = list(map(lambda perm: perm[0], USER_PERMISSIONS)) + public_permissions = [] anon_permissions = list(map(lambda perm: perm[0], ANON_PERMISSIONS)) elif membership: if membership.is_admin: diff --git a/tests/integration/resources_permissions/test_attachment_resources.py b/tests/integration/resources_permissions/test_attachment_resources.py index 0395d8b4..c48a7e3a 100644 --- a/tests/integration/resources_permissions/test_attachment_resources.py +++ b/tests/integration/resources_permissions/test_attachment_resources.py @@ -4,7 +4,7 @@ from django.test.client import MULTIPART_CONTENT from taiga.base.utils import json -from taiga.permissions.permissions import MEMBERS_PERMISSIONS, ANON_PERMISSIONS, USER_PERMISSIONS +from taiga.permissions.permissions import MEMBERS_PERMISSIONS, ANON_PERMISSIONS from taiga.projects import choices as project_choices from taiga.projects.attachments.serializers import AttachmentSerializer @@ -38,11 +38,11 @@ def data(): m.public_project = f.ProjectFactory(is_private=False, anon_permissions=list(map(lambda x: x[0], ANON_PERMISSIONS)), - public_permissions=list(map(lambda x: x[0], USER_PERMISSIONS)), + public_permissions=list(map(lambda x: x[0], ANON_PERMISSIONS)), owner=m.project_owner) m.private_project1 = f.ProjectFactory(is_private=True, anon_permissions=list(map(lambda x: x[0], ANON_PERMISSIONS)), - public_permissions=list(map(lambda x: x[0], USER_PERMISSIONS)), + public_permissions=list(map(lambda x: x[0], ANON_PERMISSIONS)), owner=m.project_owner) m.private_project2 = f.ProjectFactory(is_private=True, anon_permissions=[], @@ -491,9 +491,9 @@ def test_wiki_attachment_patch(client, data, data_wiki): attachment_data = json.dumps(attachment_data) results = helper_test_http_method(client, 'patch', public_url, attachment_data, users) - assert results == [401, 200, 200, 200, 200] + assert results == [401, 403, 403, 200, 200] results = helper_test_http_method(client, 'patch', private_url1, attachment_data, users) - assert results == [401, 200, 200, 200, 200] + assert results == [401, 403, 403, 200, 200] results = helper_test_http_method(client, 'patch', private_url2, attachment_data, users) assert results == [401, 403, 403, 200, 200] results = helper_test_http_method(client, 'patch', blocked_url, attachment_data, users) @@ -583,9 +583,9 @@ def test_wiki_attachment_delete(client, data, data_wiki): ] results = helper_test_http_method(client, 'delete', public_url, None, [None, data.registered_user]) - assert results == [401, 204] + assert results == [401, 403] results = helper_test_http_method(client, 'delete', private_url1, None, [None, data.registered_user]) - assert results == [401, 204] + assert results == [401, 403] results = helper_test_http_method(client, 'delete', private_url2, None, users) assert results == [401, 403, 403, 204] results = helper_test_http_method(client, 'delete', blocked_url, None, users) @@ -721,7 +721,7 @@ def test_wiki_attachment_create(client, data, data_wiki): content_type=MULTIPART_CONTENT, after_each_request=_after_each_request_hook) - assert results == [401, 201, 201, 201, 201] + assert results == [401, 403, 403, 201, 201] attachment_data = {"description": "test", "object_id": data_wiki.blocked_wiki_attachment.object_id, diff --git a/tests/integration/resources_permissions/test_history_resources.py b/tests/integration/resources_permissions/test_history_resources.py index be17510c..9d39b9d7 100644 --- a/tests/integration/resources_permissions/test_history_resources.py +++ b/tests/integration/resources_permissions/test_history_resources.py @@ -2,7 +2,7 @@ from django.core.urlresolvers import reverse from django.utils import timezone from taiga.base.utils import json -from taiga.permissions.permissions import MEMBERS_PERMISSIONS, ANON_PERMISSIONS, USER_PERMISSIONS +from taiga.permissions.permissions import MEMBERS_PERMISSIONS, ANON_PERMISSIONS from taiga.projects.history.models import HistoryEntry from taiga.projects.history.choices import HistoryType from taiga.projects.history.services import make_key_from_model_object @@ -34,11 +34,11 @@ def data(): m.public_project = f.ProjectFactory(is_private=False, anon_permissions=list(map(lambda x: x[0], ANON_PERMISSIONS)), - public_permissions=list(map(lambda x: x[0], USER_PERMISSIONS)), + public_permissions=list(map(lambda x: x[0], ANON_PERMISSIONS)), owner=m.project_owner) m.private_project1 = f.ProjectFactory(is_private=True, anon_permissions=list(map(lambda x: x[0], ANON_PERMISSIONS)), - public_permissions=list(map(lambda x: x[0], USER_PERMISSIONS)), + public_permissions=list(map(lambda x: x[0], ANON_PERMISSIONS)), owner=m.project_owner) m.private_project2 = f.ProjectFactory(is_private=True, anon_permissions=[], diff --git a/tests/integration/resources_permissions/test_issues_custom_attributes_resource.py b/tests/integration/resources_permissions/test_issues_custom_attributes_resource.py index 2c90cbfc..718172df 100644 --- a/tests/integration/resources_permissions/test_issues_custom_attributes_resource.py +++ b/tests/integration/resources_permissions/test_issues_custom_attributes_resource.py @@ -22,7 +22,7 @@ from taiga.base.utils import json from taiga.projects import choices as project_choices from taiga.projects.custom_attributes import serializers from taiga.permissions.permissions import (MEMBERS_PERMISSIONS, - ANON_PERMISSIONS, USER_PERMISSIONS) + ANON_PERMISSIONS) from tests import factories as f from tests.utils import helper_test_http_method @@ -43,11 +43,11 @@ def data(): m.public_project = f.ProjectFactory(is_private=False, anon_permissions=list(map(lambda x: x[0], ANON_PERMISSIONS)), - public_permissions=list(map(lambda x: x[0], USER_PERMISSIONS)), + public_permissions=list(map(lambda x: x[0], ANON_PERMISSIONS)), owner=m.project_owner) m.private_project1 = f.ProjectFactory(is_private=True, anon_permissions=list(map(lambda x: x[0], ANON_PERMISSIONS)), - public_permissions=list(map(lambda x: x[0], USER_PERMISSIONS)), + public_permissions=list(map(lambda x: x[0], ANON_PERMISSIONS)), owner=m.project_owner) m.private_project2 = f.ProjectFactory(is_private=True, anon_permissions=[], diff --git a/tests/integration/resources_permissions/test_issues_resources.py b/tests/integration/resources_permissions/test_issues_resources.py index 2a6d3974..4cf3e2a1 100644 --- a/tests/integration/resources_permissions/test_issues_resources.py +++ b/tests/integration/resources_permissions/test_issues_resources.py @@ -4,7 +4,7 @@ from django.core.urlresolvers import reverse from taiga.projects import choices as project_choices from taiga.projects.issues.serializers import IssueSerializer -from taiga.permissions.permissions import MEMBERS_PERMISSIONS, ANON_PERMISSIONS, USER_PERMISSIONS +from taiga.permissions.permissions import MEMBERS_PERMISSIONS, ANON_PERMISSIONS from taiga.base.utils import json from tests import factories as f @@ -39,12 +39,12 @@ def data(): m.public_project = f.ProjectFactory(is_private=False, anon_permissions=list(map(lambda x: x[0], ANON_PERMISSIONS)), - public_permissions=list(map(lambda x: x[0], USER_PERMISSIONS)), + public_permissions=list(map(lambda x: x[0], ANON_PERMISSIONS)), owner=m.project_owner, issues_csv_uuid=uuid.uuid4().hex) m.private_project1 = f.ProjectFactory(is_private=True, anon_permissions=list(map(lambda x: x[0], ANON_PERMISSIONS)), - public_permissions=list(map(lambda x: x[0], USER_PERMISSIONS)), + public_permissions=list(map(lambda x: x[0], ANON_PERMISSIONS)), owner=m.project_owner, issues_csv_uuid=uuid.uuid4().hex) m.private_project2 = f.ProjectFactory(is_private=True, @@ -402,7 +402,7 @@ def test_issue_create(client, data): "type": data.public_project.issue_types.all()[0].pk, }) results = helper_test_http_method(client, 'post', url, create_data, users) - assert results == [401, 201, 201, 201, 201] + assert results == [401, 403, 403, 201, 201] create_data = json.dumps({ "subject": "test", @@ -414,7 +414,7 @@ def test_issue_create(client, data): "type": data.private_project1.issue_types.all()[0].pk, }) results = helper_test_http_method(client, 'post', url, create_data, users) - assert results == [401, 201, 201, 201, 201] + assert results == [401, 403, 403, 201, 201] create_data = json.dumps({ "subject": "test", @@ -456,21 +456,21 @@ def test_issue_patch(client, data): ] with mock.patch.object(OCCResourceMixin, "_validate_and_update_version"): - patch_data = json.dumps({"subject": "test", "version": data.public_issue.version}) - results = helper_test_http_method(client, 'patch', public_url, patch_data, users) - assert results == [401, 403, 403, 200, 200] + patch_data = json.dumps({"subject": "test", "version": data.public_issue.version}) + results = helper_test_http_method(client, 'patch', public_url, patch_data, users) + assert results == [401, 403, 403, 200, 200] - patch_data = json.dumps({"subject": "test", "version": data.private_issue1.version}) - results = helper_test_http_method(client, 'patch', private_url1, patch_data, users) - assert results == [401, 403, 403, 200, 200] + patch_data = json.dumps({"subject": "test", "version": data.private_issue1.version}) + results = helper_test_http_method(client, 'patch', private_url1, patch_data, users) + assert results == [401, 403, 403, 200, 200] - patch_data = json.dumps({"subject": "test", "version": data.private_issue2.version}) - results = helper_test_http_method(client, 'patch', private_url2, patch_data, users) - assert results == [401, 403, 403, 200, 200] + patch_data = json.dumps({"subject": "test", "version": data.private_issue2.version}) + results = helper_test_http_method(client, 'patch', private_url2, patch_data, users) + assert results == [401, 403, 403, 200, 200] - patch_data = json.dumps({"subject": "test", "version": data.blocked_issue.version}) - results = helper_test_http_method(client, 'patch', blocked_url, patch_data, users) - assert results == [401, 403, 403, 451, 451] + patch_data = json.dumps({"subject": "test", "version": data.blocked_issue.version}) + results = helper_test_http_method(client, 'patch', blocked_url, patch_data, users) + assert results == [401, 403, 403, 451, 451] def test_issue_bulk_create(client, data): @@ -511,12 +511,12 @@ def test_issue_bulk_create(client, data): bulk_data = json.dumps({"bulk_issues": "test1\ntest2", "project_id": data.public_issue.project.pk}) results = helper_test_http_method(client, 'post', url, bulk_data, users) - assert results == [401, 200, 200, 200, 200] + assert results == [401, 403, 403, 200, 200] bulk_data = json.dumps({"bulk_issues": "test1\ntest2", "project_id": data.private_issue1.project.pk}) results = helper_test_http_method(client, 'post', url, bulk_data, users) - assert results == [401, 200, 200, 200, 200] + assert results == [401, 403, 403, 200, 200] bulk_data = json.dumps({"bulk_issues": "test1\ntest2", "project_id": data.private_issue2.project.pk}) diff --git a/tests/integration/resources_permissions/test_milestones_resources.py b/tests/integration/resources_permissions/test_milestones_resources.py index 5a6f26a4..c42c0580 100644 --- a/tests/integration/resources_permissions/test_milestones_resources.py +++ b/tests/integration/resources_permissions/test_milestones_resources.py @@ -6,7 +6,7 @@ from taiga.projects import choices as project_choices from taiga.projects.milestones.serializers import MilestoneSerializer from taiga.projects.milestones.models import Milestone from taiga.projects.notifications.services import add_watcher -from taiga.permissions.permissions import MEMBERS_PERMISSIONS, ANON_PERMISSIONS, USER_PERMISSIONS +from taiga.permissions.permissions import MEMBERS_PERMISSIONS, ANON_PERMISSIONS from tests import factories as f from tests.utils import helper_test_http_method, disconnect_signals, reconnect_signals @@ -35,11 +35,11 @@ def data(): m.public_project = f.ProjectFactory(is_private=False, anon_permissions=list(map(lambda x: x[0], ANON_PERMISSIONS)), - public_permissions=list(map(lambda x: x[0], USER_PERMISSIONS)), + public_permissions=list(map(lambda x: x[0], ANON_PERMISSIONS)), owner=m.project_owner) m.private_project1 = f.ProjectFactory(is_private=True, anon_permissions=list(map(lambda x: x[0], ANON_PERMISSIONS)), - public_permissions=list(map(lambda x: x[0], USER_PERMISSIONS)), + public_permissions=list(map(lambda x: x[0], ANON_PERMISSIONS)), owner=m.project_owner) m.private_project2 = f.ProjectFactory(is_private=True, anon_permissions=[], diff --git a/tests/integration/resources_permissions/test_modules_resources.py b/tests/integration/resources_permissions/test_modules_resources.py index 8260bd2f..27269458 100644 --- a/tests/integration/resources_permissions/test_modules_resources.py +++ b/tests/integration/resources_permissions/test_modules_resources.py @@ -2,7 +2,7 @@ import uuid from django.core.urlresolvers import reverse -from taiga.permissions.permissions import MEMBERS_PERMISSIONS, ANON_PERMISSIONS, USER_PERMISSIONS +from taiga.permissions.permissions import MEMBERS_PERMISSIONS, ANON_PERMISSIONS from taiga.base.utils import json from tests import factories as f @@ -38,11 +38,11 @@ def data(): m.public_project = f.ProjectFactory(is_private=False, anon_permissions=list(map(lambda x: x[0], ANON_PERMISSIONS)), - public_permissions=list(map(lambda x: x[0], USER_PERMISSIONS)), + public_permissions=list(map(lambda x: x[0], ANON_PERMISSIONS)), owner=m.project_owner) m.private_project1 = f.ProjectFactory(is_private=True, anon_permissions=list(map(lambda x: x[0], ANON_PERMISSIONS)), - public_permissions=list(map(lambda x: x[0], USER_PERMISSIONS)), + public_permissions=list(map(lambda x: x[0], ANON_PERMISSIONS)), owner=m.project_owner) m.private_project2 = f.ProjectFactory(is_private=True, anon_permissions=[], diff --git a/tests/integration/resources_permissions/test_resolver_resources.py b/tests/integration/resources_permissions/test_resolver_resources.py index f878ca14..6858d976 100644 --- a/tests/integration/resources_permissions/test_resolver_resources.py +++ b/tests/integration/resources_permissions/test_resolver_resources.py @@ -1,6 +1,6 @@ from django.core.urlresolvers import reverse -from taiga.permissions.permissions import MEMBERS_PERMISSIONS, ANON_PERMISSIONS, USER_PERMISSIONS +from taiga.permissions.permissions import MEMBERS_PERMISSIONS, ANON_PERMISSIONS from tests import factories as f from tests.utils import helper_test_http_method, disconnect_signals, reconnect_signals @@ -29,12 +29,12 @@ def data(): m.public_project = f.ProjectFactory(is_private=False, anon_permissions=list(map(lambda x: x[0], ANON_PERMISSIONS)), - public_permissions=list(map(lambda x: x[0], USER_PERMISSIONS)), + public_permissions=list(map(lambda x: x[0], ANON_PERMISSIONS)), owner=m.project_owner, slug="public") m.private_project1 = f.ProjectFactory(is_private=True, anon_permissions=list(map(lambda x: x[0], ANON_PERMISSIONS)), - public_permissions=list(map(lambda x: x[0], USER_PERMISSIONS)), + public_permissions=list(map(lambda x: x[0], ANON_PERMISSIONS)), owner=m.project_owner, slug="private1") m.private_project2 = f.ProjectFactory(is_private=True, diff --git a/tests/integration/resources_permissions/test_search_resources.py b/tests/integration/resources_permissions/test_search_resources.py index 8d3d9442..783818d3 100644 --- a/tests/integration/resources_permissions/test_search_resources.py +++ b/tests/integration/resources_permissions/test_search_resources.py @@ -1,6 +1,6 @@ from django.core.urlresolvers import reverse -from taiga.permissions.permissions import MEMBERS_PERMISSIONS, ANON_PERMISSIONS, USER_PERMISSIONS +from taiga.permissions.permissions import MEMBERS_PERMISSIONS, ANON_PERMISSIONS from tests import factories as f from tests.utils import helper_test_http_method_and_keys, disconnect_signals, reconnect_signals @@ -29,11 +29,11 @@ def data(): m.public_project = f.ProjectFactory(is_private=False, anon_permissions=list(map(lambda x: x[0], ANON_PERMISSIONS)), - public_permissions=list(map(lambda x: x[0], USER_PERMISSIONS)), + public_permissions=list(map(lambda x: x[0], ANON_PERMISSIONS)), owner=m.project_owner) m.private_project1 = f.ProjectFactory(is_private=True, anon_permissions=list(map(lambda x: x[0], ANON_PERMISSIONS)), - public_permissions=list(map(lambda x: x[0], USER_PERMISSIONS)), + public_permissions=list(map(lambda x: x[0], ANON_PERMISSIONS)), owner=m.project_owner) m.private_project2 = f.ProjectFactory(is_private=True, anon_permissions=[], diff --git a/tests/integration/resources_permissions/test_tasks_custom_attributes_resource.py b/tests/integration/resources_permissions/test_tasks_custom_attributes_resource.py index 1fd33e46..44509354 100644 --- a/tests/integration/resources_permissions/test_tasks_custom_attributes_resource.py +++ b/tests/integration/resources_permissions/test_tasks_custom_attributes_resource.py @@ -22,7 +22,7 @@ from taiga.base.utils import json from taiga.projects import choices as project_choices from taiga.projects.custom_attributes import serializers from taiga.permissions.permissions import (MEMBERS_PERMISSIONS, - ANON_PERMISSIONS, USER_PERMISSIONS) + ANON_PERMISSIONS) from tests import factories as f from tests.utils import helper_test_http_method @@ -43,11 +43,11 @@ def data(): m.public_project = f.ProjectFactory(is_private=False, anon_permissions=list(map(lambda x: x[0], ANON_PERMISSIONS)), - public_permissions=list(map(lambda x: x[0], USER_PERMISSIONS)), + public_permissions=list(map(lambda x: x[0], ANON_PERMISSIONS)), owner=m.project_owner) m.private_project1 = f.ProjectFactory(is_private=True, anon_permissions=list(map(lambda x: x[0], ANON_PERMISSIONS)), - public_permissions=list(map(lambda x: x[0], USER_PERMISSIONS)), + public_permissions=list(map(lambda x: x[0], ANON_PERMISSIONS)), owner=m.project_owner) m.private_project2 = f.ProjectFactory(is_private=True, anon_permissions=[], diff --git a/tests/integration/resources_permissions/test_tasks_resources.py b/tests/integration/resources_permissions/test_tasks_resources.py index 4771d12c..274f7a0c 100644 --- a/tests/integration/resources_permissions/test_tasks_resources.py +++ b/tests/integration/resources_permissions/test_tasks_resources.py @@ -5,7 +5,7 @@ from django.core.urlresolvers import reverse from taiga.base.utils import json from taiga.projects import choices as project_choices from taiga.projects.tasks.serializers import TaskSerializer -from taiga.permissions.permissions import MEMBERS_PERMISSIONS, ANON_PERMISSIONS, USER_PERMISSIONS +from taiga.permissions.permissions import MEMBERS_PERMISSIONS, ANON_PERMISSIONS from taiga.projects.occ import OCCResourceMixin from tests import factories as f @@ -39,12 +39,12 @@ def data(): m.public_project = f.ProjectFactory(is_private=False, anon_permissions=list(map(lambda x: x[0], ANON_PERMISSIONS)), - public_permissions=list(map(lambda x: x[0], USER_PERMISSIONS)), + public_permissions=list(map(lambda x: x[0], ANON_PERMISSIONS)), owner=m.project_owner, tasks_csv_uuid=uuid.uuid4().hex) m.private_project1 = f.ProjectFactory(is_private=True, anon_permissions=list(map(lambda x: x[0], ANON_PERMISSIONS)), - public_permissions=list(map(lambda x: x[0], USER_PERMISSIONS)), + public_permissions=list(map(lambda x: x[0], ANON_PERMISSIONS)), owner=m.project_owner, tasks_csv_uuid=uuid.uuid4().hex) m.private_project2 = f.ProjectFactory(is_private=True, diff --git a/tests/integration/resources_permissions/test_timelines_resources.py b/tests/integration/resources_permissions/test_timelines_resources.py index 0a874443..ffc4e41d 100644 --- a/tests/integration/resources_permissions/test_timelines_resources.py +++ b/tests/integration/resources_permissions/test_timelines_resources.py @@ -1,6 +1,6 @@ from django.core.urlresolvers import reverse -from taiga.permissions.permissions import MEMBERS_PERMISSIONS, ANON_PERMISSIONS, USER_PERMISSIONS +from taiga.permissions.permissions import MEMBERS_PERMISSIONS, ANON_PERMISSIONS from tests import factories as f from tests.utils import helper_test_http_method, disconnect_signals, reconnect_signals @@ -29,11 +29,11 @@ def data(): m.public_project = f.ProjectFactory(is_private=False, anon_permissions=list(map(lambda x: x[0], ANON_PERMISSIONS)), - public_permissions=list(map(lambda x: x[0], USER_PERMISSIONS)), + public_permissions=list(map(lambda x: x[0], ANON_PERMISSIONS)), owner=m.project_owner) m.private_project1 = f.ProjectFactory(is_private=True, anon_permissions=list(map(lambda x: x[0], ANON_PERMISSIONS)), - public_permissions=list(map(lambda x: x[0], USER_PERMISSIONS)), + public_permissions=list(map(lambda x: x[0], ANON_PERMISSIONS)), owner=m.project_owner) m.private_project2 = f.ProjectFactory(is_private=True, anon_permissions=[], diff --git a/tests/integration/resources_permissions/test_userstories_custom_attributes_resource.py b/tests/integration/resources_permissions/test_userstories_custom_attributes_resource.py index 9e6bd6ff..030060f3 100644 --- a/tests/integration/resources_permissions/test_userstories_custom_attributes_resource.py +++ b/tests/integration/resources_permissions/test_userstories_custom_attributes_resource.py @@ -22,7 +22,7 @@ from taiga.base.utils import json from taiga.projects import choices as project_choices from taiga.projects.custom_attributes import serializers from taiga.permissions.permissions import (MEMBERS_PERMISSIONS, - ANON_PERMISSIONS, USER_PERMISSIONS) + ANON_PERMISSIONS) from tests import factories as f @@ -44,11 +44,11 @@ def data(): m.public_project = f.ProjectFactory(is_private=False, anon_permissions=list(map(lambda x: x[0], ANON_PERMISSIONS)), - public_permissions=list(map(lambda x: x[0], USER_PERMISSIONS)), + public_permissions=list(map(lambda x: x[0], ANON_PERMISSIONS)), owner=m.project_owner) m.private_project1 = f.ProjectFactory(is_private=True, anon_permissions=list(map(lambda x: x[0], ANON_PERMISSIONS)), - public_permissions=list(map(lambda x: x[0], USER_PERMISSIONS)), + public_permissions=list(map(lambda x: x[0], ANON_PERMISSIONS)), owner=m.project_owner) m.private_project2 = f.ProjectFactory(is_private=True, anon_permissions=[], diff --git a/tests/integration/resources_permissions/test_userstories_resources.py b/tests/integration/resources_permissions/test_userstories_resources.py index 80559a87..0daadad5 100644 --- a/tests/integration/resources_permissions/test_userstories_resources.py +++ b/tests/integration/resources_permissions/test_userstories_resources.py @@ -5,7 +5,7 @@ from django.core.urlresolvers import reverse from taiga.base.utils import json from taiga.projects import choices as project_choices from taiga.projects.userstories.serializers import UserStorySerializer -from taiga.permissions.permissions import MEMBERS_PERMISSIONS, ANON_PERMISSIONS, USER_PERMISSIONS +from taiga.permissions.permissions import MEMBERS_PERMISSIONS, ANON_PERMISSIONS from taiga.projects.occ import OCCResourceMixin from tests import factories as f @@ -39,12 +39,12 @@ def data(): m.public_project = f.ProjectFactory(is_private=False, anon_permissions=list(map(lambda x: x[0], ANON_PERMISSIONS)), - public_permissions=list(map(lambda x: x[0], USER_PERMISSIONS)), + public_permissions=list(map(lambda x: x[0], ANON_PERMISSIONS)), owner=m.project_owner, userstories_csv_uuid=uuid.uuid4().hex) m.private_project1 = f.ProjectFactory(is_private=True, anon_permissions=list(map(lambda x: x[0], ANON_PERMISSIONS)), - public_permissions=list(map(lambda x: x[0], USER_PERMISSIONS)), + public_permissions=list(map(lambda x: x[0], ANON_PERMISSIONS)), owner=m.project_owner, userstories_csv_uuid=uuid.uuid4().hex) m.private_project2 = f.ProjectFactory(is_private=True, @@ -177,29 +177,29 @@ def test_user_story_update(client, data): ] with mock.patch.object(OCCResourceMixin, "_validate_and_update_version"): - user_story_data = UserStorySerializer(data.public_user_story).data - user_story_data["subject"] = "test" - user_story_data = json.dumps(user_story_data) - results = helper_test_http_method(client, 'put', public_url, user_story_data, users) - assert results == [401, 403, 403, 200, 200] + user_story_data = UserStorySerializer(data.public_user_story).data + user_story_data["subject"] = "test" + user_story_data = json.dumps(user_story_data) + results = helper_test_http_method(client, 'put', public_url, user_story_data, users) + assert results == [401, 403, 403, 200, 200] - user_story_data = UserStorySerializer(data.private_user_story1).data - user_story_data["subject"] = "test" - user_story_data = json.dumps(user_story_data) - results = helper_test_http_method(client, 'put', private_url1, user_story_data, users) - assert results == [401, 403, 403, 200, 200] + user_story_data = UserStorySerializer(data.private_user_story1).data + user_story_data["subject"] = "test" + user_story_data = json.dumps(user_story_data) + results = helper_test_http_method(client, 'put', private_url1, user_story_data, users) + assert results == [401, 403, 403, 200, 200] - user_story_data = UserStorySerializer(data.private_user_story2).data - user_story_data["subject"] = "test" - user_story_data = json.dumps(user_story_data) - results = helper_test_http_method(client, 'put', private_url2, user_story_data, users) - assert results == [401, 403, 403, 200, 200] + user_story_data = UserStorySerializer(data.private_user_story2).data + user_story_data["subject"] = "test" + user_story_data = json.dumps(user_story_data) + results = helper_test_http_method(client, 'put', private_url2, user_story_data, users) + assert results == [401, 403, 403, 200, 200] - user_story_data = UserStorySerializer(data.blocked_user_story).data - user_story_data["subject"] = "test" - user_story_data = json.dumps(user_story_data) - results = helper_test_http_method(client, 'put', blocked_url, user_story_data, users) - assert results == [401, 403, 403, 451, 451] + user_story_data = UserStorySerializer(data.blocked_user_story).data + user_story_data["subject"] = "test" + user_story_data = json.dumps(user_story_data) + results = helper_test_http_method(client, 'put', blocked_url, user_story_data, users) + assert results == [401, 403, 403, 451, 451] def test_user_story_update_with_project_change(client): user1 = f.UserFactory.create() @@ -361,11 +361,11 @@ def test_user_story_create(client, data): create_data = json.dumps({"subject": "test", "ref": 1, "project": data.public_project.pk}) results = helper_test_http_method(client, 'post', url, create_data, users) - assert results == [401, 201, 201, 201, 201] + assert results == [401, 403, 403, 201, 201] create_data = json.dumps({"subject": "test", "ref": 2, "project": data.private_project1.pk}) results = helper_test_http_method(client, 'post', url, create_data, users) - assert results == [401, 201, 201, 201, 201] + assert results == [401, 403, 403, 201, 201] create_data = json.dumps({"subject": "test", "ref": 3, "project": data.private_project2.pk}) results = helper_test_http_method(client, 'post', url, create_data, users) @@ -391,21 +391,21 @@ def test_user_story_patch(client, data): ] with mock.patch.object(OCCResourceMixin, "_validate_and_update_version"): - patch_data = json.dumps({"subject": "test", "version": data.public_user_story.version}) - results = helper_test_http_method(client, 'patch', public_url, patch_data, users) - assert results == [401, 403, 403, 200, 200] + patch_data = json.dumps({"subject": "test", "version": data.public_user_story.version}) + results = helper_test_http_method(client, 'patch', public_url, patch_data, users) + assert results == [401, 403, 403, 200, 200] - patch_data = json.dumps({"subject": "test", "version": data.private_user_story1.version}) - results = helper_test_http_method(client, 'patch', private_url1, patch_data, users) - assert results == [401, 403, 403, 200, 200] + patch_data = json.dumps({"subject": "test", "version": data.private_user_story1.version}) + results = helper_test_http_method(client, 'patch', private_url1, patch_data, users) + assert results == [401, 403, 403, 200, 200] - patch_data = json.dumps({"subject": "test", "version": data.private_user_story2.version}) - results = helper_test_http_method(client, 'patch', private_url2, patch_data, users) - assert results == [401, 403, 403, 200, 200] + patch_data = json.dumps({"subject": "test", "version": data.private_user_story2.version}) + results = helper_test_http_method(client, 'patch', private_url2, patch_data, users) + assert results == [401, 403, 403, 200, 200] - patch_data = json.dumps({"subject": "test", "version": data.blocked_user_story.version}) - results = helper_test_http_method(client, 'patch', blocked_url, patch_data, users) - assert results == [401, 403, 403, 451, 451] + patch_data = json.dumps({"subject": "test", "version": data.blocked_user_story.version}) + results = helper_test_http_method(client, 'patch', blocked_url, patch_data, users) + assert results == [401, 403, 403, 451, 451] def test_user_story_action_bulk_create(client, data): @@ -421,11 +421,11 @@ def test_user_story_action_bulk_create(client, data): bulk_data = json.dumps({"bulk_stories": "test1\ntest2", "project_id": data.public_user_story.project.pk}) results = helper_test_http_method(client, 'post', url, bulk_data, users) - assert results == [401, 200, 200, 200, 200] + assert results == [401, 403, 403, 200, 200] bulk_data = json.dumps({"bulk_stories": "test1\ntest2", "project_id": data.private_user_story1.project.pk}) results = helper_test_http_method(client, 'post', url, bulk_data, users) - assert results == [401, 200, 200, 200, 200] + assert results == [401, 403, 403, 200, 200] bulk_data = json.dumps({"bulk_stories": "test1\ntest2", "project_id": data.private_user_story2.project.pk}) results = helper_test_http_method(client, 'post', url, bulk_data, users) diff --git a/tests/integration/resources_permissions/test_wiki_resources.py b/tests/integration/resources_permissions/test_wiki_resources.py index c69fb2bc..ed76e769 100644 --- a/tests/integration/resources_permissions/test_wiki_resources.py +++ b/tests/integration/resources_permissions/test_wiki_resources.py @@ -1,7 +1,7 @@ from django.core.urlresolvers import reverse from taiga.base.utils import json -from taiga.permissions.permissions import MEMBERS_PERMISSIONS, ANON_PERMISSIONS, USER_PERMISSIONS +from taiga.permissions.permissions import MEMBERS_PERMISSIONS, ANON_PERMISSIONS from taiga.projects import choices as project_choices from taiga.projects.notifications.services import add_watcher from taiga.projects.occ import OCCResourceMixin @@ -37,11 +37,11 @@ def data(): m.public_project = f.ProjectFactory(is_private=False, anon_permissions=list(map(lambda x: x[0], ANON_PERMISSIONS)), - public_permissions=list(map(lambda x: x[0], USER_PERMISSIONS)), + public_permissions=list(map(lambda x: x[0], ANON_PERMISSIONS)), owner=m.project_owner) m.private_project1 = f.ProjectFactory(is_private=True, anon_permissions=list(map(lambda x: x[0], ANON_PERMISSIONS)), - public_permissions=list(map(lambda x: x[0], USER_PERMISSIONS)), + public_permissions=list(map(lambda x: x[0], ANON_PERMISSIONS)), owner=m.project_owner) m.private_project2 = f.ProjectFactory(is_private=True, anon_permissions=[], @@ -154,13 +154,13 @@ def test_wiki_page_update(client, data): wiki_page_data["content"] = "test" wiki_page_data = json.dumps(wiki_page_data) results = helper_test_http_method(client, 'put', public_url, wiki_page_data, users) - assert results == [401, 200, 200, 200, 200] + assert results == [401, 403, 403, 200, 200] wiki_page_data = WikiPageSerializer(data.private_wiki_page1).data wiki_page_data["content"] = "test" wiki_page_data = json.dumps(wiki_page_data) results = helper_test_http_method(client, 'put', private_url1, wiki_page_data, users) - assert results == [401, 200, 200, 200, 200] + assert results == [401, 403, 403, 200, 200] wiki_page_data = WikiPageSerializer(data.private_wiki_page2).data wiki_page_data["content"] = "test" @@ -244,7 +244,7 @@ def test_wiki_page_create(client, data): "project": data.public_project.pk, }) results = helper_test_http_method(client, 'post', url, create_data, users, lambda: WikiPage.objects.all().delete()) - assert results == [401, 201, 201, 201, 201] + assert results == [401, 403, 403, 201, 201] create_data = json.dumps({ "content": "test", @@ -252,7 +252,7 @@ def test_wiki_page_create(client, data): "project": data.private_project1.pk, }) results = helper_test_http_method(client, 'post', url, create_data, users, lambda: WikiPage.objects.all().delete()) - assert results == [401, 201, 201, 201, 201] + assert results == [401, 403, 403, 201, 201] create_data = json.dumps({ "content": "test", @@ -287,11 +287,11 @@ def test_wiki_page_patch(client, data): with mock.patch.object(OCCResourceMixin, "_validate_and_update_version"): patch_data = json.dumps({"content": "test", "version": data.public_wiki_page.version}) results = helper_test_http_method(client, 'patch', public_url, patch_data, users) - assert results == [401, 200, 200, 200, 200] + assert results == [401, 403, 403, 200, 200] patch_data = json.dumps({"content": "test", "version": data.private_wiki_page2.version}) results = helper_test_http_method(client, 'patch', private_url1, patch_data, users) - assert results == [401, 200, 200, 200, 200] + assert results == [401, 403, 403, 200, 200] patch_data = json.dumps({"content": "test", "version": data.private_wiki_page2.version}) results = helper_test_http_method(client, 'patch', private_url2, patch_data, users) @@ -361,13 +361,13 @@ def test_wiki_link_update(client, data): wiki_link_data["title"] = "test" wiki_link_data = json.dumps(wiki_link_data) results = helper_test_http_method(client, 'put', public_url, wiki_link_data, users) - assert results == [401, 200, 200, 200, 200] + assert results == [401, 403, 403, 200, 200] wiki_link_data = WikiLinkSerializer(data.private_wiki_link1).data wiki_link_data["title"] = "test" wiki_link_data = json.dumps(wiki_link_data) results = helper_test_http_method(client, 'put', private_url1, wiki_link_data, users) - assert results == [401, 200, 200, 200, 200] + assert results == [401, 403, 403, 200, 200] wiki_link_data = WikiLinkSerializer(data.private_wiki_link2).data wiki_link_data["title"] = "test" @@ -450,7 +450,7 @@ def test_wiki_link_create(client, data): "project": data.public_project.pk, }) results = helper_test_http_method(client, 'post', url, create_data, users, lambda: WikiLink.objects.all().delete()) - assert results == [401, 201, 201, 201, 201] + assert results == [401, 403, 403, 201, 201] create_data = json.dumps({ "title": "test", @@ -458,7 +458,7 @@ def test_wiki_link_create(client, data): "project": data.private_project1.pk, }) results = helper_test_http_method(client, 'post', url, create_data, users, lambda: WikiLink.objects.all().delete()) - assert results == [401, 201, 201, 201, 201] + assert results == [401, 403, 403, 201, 201] create_data = json.dumps({ "title": "test", @@ -492,21 +492,21 @@ def test_wiki_link_patch(client, data): ] with mock.patch.object(OCCResourceMixin, "_validate_and_update_version"): - patch_data = json.dumps({"title": "test"}) - results = helper_test_http_method(client, 'patch', public_url, patch_data, users) - assert results == [401, 200, 200, 200, 200] + patch_data = json.dumps({"title": "test"}) + results = helper_test_http_method(client, 'patch', public_url, patch_data, users) + assert results == [401, 403, 403, 200, 200] - patch_data = json.dumps({"title": "test"}) - results = helper_test_http_method(client, 'patch', private_url1, patch_data, users) - assert results == [401, 200, 200, 200, 200] + patch_data = json.dumps({"title": "test"}) + results = helper_test_http_method(client, 'patch', private_url1, patch_data, users) + assert results == [401, 403, 403, 200, 200] - patch_data = json.dumps({"title": "test"}) - results = helper_test_http_method(client, 'patch', private_url2, patch_data, users) - assert results == [401, 403, 403, 200, 200] + patch_data = json.dumps({"title": "test"}) + results = helper_test_http_method(client, 'patch', private_url2, patch_data, users) + assert results == [401, 403, 403, 200, 200] - patch_data = json.dumps({"title": "test"}) - results = helper_test_http_method(client, 'patch', blocked_url, patch_data, users) - assert results == [401, 403, 403, 451, 451] + patch_data = json.dumps({"title": "test"}) + results = helper_test_http_method(client, 'patch', blocked_url, patch_data, users) + assert results == [401, 403, 403, 451, 451] def test_wikipage_action_watch(client, data): diff --git a/tests/integration/test_users.py b/tests/integration/test_users.py index 4e9da6e5..7ca9b867 100644 --- a/tests/integration/test_users.py +++ b/tests/integration/test_users.py @@ -14,7 +14,7 @@ from taiga.base.utils.thumbnails import get_thumbnail_url from taiga.users import models from taiga.users.serializers import LikedObjectSerializer, VotedObjectSerializer from taiga.auth.tokens import get_token_for_user -from taiga.permissions.permissions import MEMBERS_PERMISSIONS, ANON_PERMISSIONS, USER_PERMISSIONS +from taiga.permissions.permissions import MEMBERS_PERMISSIONS, ANON_PERMISSIONS from taiga.projects import choices as project_choices from taiga.users.services import get_watched_list, get_voted_list, get_liked_list from taiga.projects.notifications.choices import NotifyLevel @@ -340,7 +340,7 @@ def test_list_contacts_no_projects(client): def test_list_contacts_public_projects(client): project = f.ProjectFactory.create(is_private=False, anon_permissions=list(map(lambda x: x[0], ANON_PERMISSIONS)), - public_permissions=list(map(lambda x: x[0], USER_PERMISSIONS))) + public_permissions=list(map(lambda x: x[0], ANON_PERMISSIONS))) user_1 = f.UserFactory.create() user_2 = f.UserFactory.create() diff --git a/tests/integration/test_watch_projects.py b/tests/integration/test_watch_projects.py index 2608864b..6c4f7d41 100644 --- a/tests/integration/test_watch_projects.py +++ b/tests/integration/test_watch_projects.py @@ -20,7 +20,7 @@ import pytest import json from django.core.urlresolvers import reverse -from taiga.permissions.permissions import MEMBERS_PERMISSIONS, ANON_PERMISSIONS, USER_PERMISSIONS +from taiga.permissions.permissions import MEMBERS_PERMISSIONS, ANON_PERMISSIONS from .. import factories as f @@ -129,7 +129,7 @@ def test_get_project_is_watcher(client): user = f.UserFactory.create() project = f.ProjectFactory.create(is_private=False, anon_permissions=list(map(lambda x: x[0], ANON_PERMISSIONS)), - public_permissions=list(map(lambda x: x[0], USER_PERMISSIONS))) + public_permissions=list(map(lambda x: x[0], ANON_PERMISSIONS))) url_detail = reverse("projects-detail", args=(project.id,)) url_watch = reverse("projects-watch", args=(project.id,)) From 38e5198cc9443fb1d6c1c67a99d7c5dfafee6f27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Mon, 23 May 2016 14:57:52 +0200 Subject: [PATCH 3/4] Minor refactor over permissions module --- ...rate_fixtures_initial_project_templates.sh | 6 ++ taiga/base/api/permissions.py | 33 ++------ taiga/permissions/choices.py | 72 ++++++++++++++++ taiga/permissions/permissions.py | 83 +++++++------------ taiga/permissions/{service.py => services.py} | 9 +- taiga/projects/api.py | 12 +-- taiga/projects/history/permissions.py | 2 +- taiga/projects/issues/permissions.py | 14 +--- .../management/commands/sample_data.py | 2 +- taiga/projects/models.py | 11 ++- taiga/projects/notifications/services.py | 2 +- taiga/projects/permissions.py | 25 +++--- taiga/projects/references/api.py | 2 +- taiga/projects/serializers.py | 4 +- taiga/projects/signals.py | 1 - taiga/projects/tasks/permissions.py | 5 +- taiga/projects/userstories/api.py | 1 - taiga/projects/userstories/permissions.py | 7 +- taiga/searches/api.py | 2 +- taiga/timeline/permissions.py | 8 +- taiga/users/models.py | 2 +- taiga/webhooks/permissions.py | 2 +- tests/factories.py | 2 +- .../test_attachment_resources.py | 2 +- .../test_history_resources.py | 2 +- .../test_issues_custom_attributes_resource.py | 2 +- .../test_issues_resources.py | 2 +- .../test_milestones_resources.py | 2 +- .../test_modules_resources.py | 2 +- .../test_projects_choices_resources.py | 2 +- .../test_projects_resource.py | 2 +- .../test_resolver_resources.py | 2 +- .../test_search_resources.py | 2 +- .../test_tasks_custom_attributes_resource.py | 2 +- .../test_tasks_resources.py | 2 +- .../test_timelines_resources.py | 2 +- ..._userstories_custom_attributes_resource.py | 2 +- .../test_userstories_resources.py | 2 +- .../test_wiki_resources.py | 2 +- tests/integration/test_notifications.py | 2 +- tests/integration/test_permissions.py | 36 ++++---- tests/integration/test_projects.py | 2 +- tests/integration/test_searches.py | 2 +- tests/integration/test_users.py | 2 +- tests/integration/test_watch_projects.py | 2 +- tests/unit/test_permissions.py | 26 ------ 46 files changed, 205 insertions(+), 206 deletions(-) create mode 100755 scripts/generate_fixtures_initial_project_templates.sh create mode 100644 taiga/permissions/choices.py rename taiga/permissions/{service.py => services.py} (96%) delete mode 100644 tests/unit/test_permissions.py diff --git a/scripts/generate_fixtures_initial_project_templates.sh b/scripts/generate_fixtures_initial_project_templates.sh new file mode 100755 index 00000000..d0201489 --- /dev/null +++ b/scripts/generate_fixtures_initial_project_templates.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +python ./manage.py dumpdata --format json \ + --indent 4 \ + --output './taiga/projects/fixtures/initial_project_templates.json' \ + 'projects.ProjectTemplate' diff --git a/taiga/base/api/permissions.py b/taiga/base/api/permissions.py index 62b40619..19b366fc 100644 --- a/taiga/base/api/permissions.py +++ b/taiga/base/api/permissions.py @@ -20,11 +20,12 @@ import abc from functools import reduce from taiga.base.utils import sequence as sq -from taiga.permissions.service import user_has_perm, is_project_admin +from taiga.permissions.services import user_has_perm, is_project_admin from django.apps import apps from django.utils.translation import ugettext as _ + ###################################################################### # Base permissiones definition ###################################################################### @@ -179,33 +180,6 @@ class HasProjectPerm(PermissionComponent): return user_has_perm(request.user, self.project_perm, obj) -class HasProjectParamAndPerm(PermissionComponent): - def __init__(self, perm, *components): - self.project_perm = perm - super().__init__(*components) - - def check_permissions(self, request, view, obj=None): - Project = apps.get_model('projects', 'Project') - project_id = request.QUERY_PARAMS.get("project", None) - try: - project = Project.objects.get(pk=project_id) - except Project.DoesNotExist: - return False - return user_has_perm(request.user, self.project_perm, project) - - -class HasMandatoryParam(PermissionComponent): - def __init__(self, param, *components): - self.mandatory_param = param - super().__init__(*components) - - def check_permissions(self, request, view, obj=None): - param = request.GET.get(self.mandatory_param, None) - if param: - return True - return False - - class IsProjectAdmin(PermissionComponent): def check_permissions(self, request, view, obj=None): return is_project_admin(request.user, obj) @@ -213,6 +187,9 @@ class IsProjectAdmin(PermissionComponent): class IsObjectOwner(PermissionComponent): def check_permissions(self, request, view, obj=None): + if obj.owner is None: + return False + return obj.owner == request.user diff --git a/taiga/permissions/choices.py b/taiga/permissions/choices.py new file mode 100644 index 00000000..bfd7192e --- /dev/null +++ b/taiga/permissions/choices.py @@ -0,0 +1,72 @@ +# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Jesús Espino +# Copyright (C) 2014-2016 David Barragán +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Anler Hernández +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +from django.utils.translation import ugettext_lazy as _ + +ANON_PERMISSIONS = [ + ('view_project', _('View project')), + ('view_milestones', _('View milestones')), + ('view_us', _('View user stories')), + ('view_tasks', _('View tasks')), + ('view_issues', _('View issues')), + ('view_wiki_pages', _('View wiki pages')), + ('view_wiki_links', _('View wiki links')), +] + +MEMBERS_PERMISSIONS = [ + ('view_project', _('View project')), + # Milestone permissions + ('view_milestones', _('View milestones')), + ('add_milestone', _('Add milestone')), + ('modify_milestone', _('Modify milestone')), + ('delete_milestone', _('Delete milestone')), + # US permissions + ('view_us', _('View user story')), + ('add_us', _('Add user story')), + ('modify_us', _('Modify user story')), + ('delete_us', _('Delete user story')), + # Task permissions + ('view_tasks', _('View tasks')), + ('add_task', _('Add task')), + ('modify_task', _('Modify task')), + ('delete_task', _('Delete task')), + # Issue permissions + ('view_issues', _('View issues')), + ('add_issue', _('Add issue')), + ('modify_issue', _('Modify issue')), + ('delete_issue', _('Delete issue')), + # Wiki page permissions + ('view_wiki_pages', _('View wiki pages')), + ('add_wiki_page', _('Add wiki page')), + ('modify_wiki_page', _('Modify wiki page')), + ('delete_wiki_page', _('Delete wiki page')), + # Wiki link permissions + ('view_wiki_links', _('View wiki links')), + ('add_wiki_link', _('Add wiki link')), + ('modify_wiki_link', _('Modify wiki link')), + ('delete_wiki_link', _('Delete wiki link')), +] + +ADMINS_PERMISSIONS = [ + ('modify_project', _('Modify project')), + ('delete_project', _('Delete project')), + ('add_member', _('Add member')), + ('remove_member', _('Remove member')), + ('admin_project_values', _('Admin project values')), + ('admin_roles', _('Admin roles')), +] diff --git a/taiga/permissions/permissions.py b/taiga/permissions/permissions.py index 61c0b683..4e563522 100644 --- a/taiga/permissions/permissions.py +++ b/taiga/permissions/permissions.py @@ -16,57 +16,38 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . -from django.utils.translation import ugettext_lazy as _ +from django.apps import apps -ANON_PERMISSIONS = [ - ('view_project', _('View project')), - ('view_milestones', _('View milestones')), - ('view_us', _('View user stories')), - ('view_tasks', _('View tasks')), - ('view_issues', _('View issues')), - ('view_wiki_pages', _('View wiki pages')), - ('view_wiki_links', _('View wiki links')), -] +from taiga.base.api.permissions import PermissionComponent -MEMBERS_PERMISSIONS = [ - ('view_project', _('View project')), - # Milestone permissions - ('view_milestones', _('View milestones')), - ('add_milestone', _('Add milestone')), - ('modify_milestone', _('Modify milestone')), - ('delete_milestone', _('Delete milestone')), - # US permissions - ('view_us', _('View user story')), - ('add_us', _('Add user story')), - ('modify_us', _('Modify user story')), - ('delete_us', _('Delete user story')), - # Task permissions - ('view_tasks', _('View tasks')), - ('add_task', _('Add task')), - ('modify_task', _('Modify task')), - ('delete_task', _('Delete task')), - # Issue permissions - ('view_issues', _('View issues')), - ('add_issue', _('Add issue')), - ('modify_issue', _('Modify issue')), - ('delete_issue', _('Delete issue')), - # Wiki page permissions - ('view_wiki_pages', _('View wiki pages')), - ('add_wiki_page', _('Add wiki page')), - ('modify_wiki_page', _('Modify wiki page')), - ('delete_wiki_page', _('Delete wiki page')), - # Wiki link permissions - ('view_wiki_links', _('View wiki links')), - ('add_wiki_link', _('Add wiki link')), - ('modify_wiki_link', _('Modify wiki link')), - ('delete_wiki_link', _('Delete wiki link')), -] +from . import services -ADMINS_PERMISSIONS = [ - ('modify_project', _('Modify project')), - ('add_member', _('Add member')), - ('remove_member', _('Remove member')), - ('delete_project', _('Delete project')), - ('admin_project_values', _('Admin project values')), - ('admin_roles', _('Admin roles')), -] + +###################################################################### +# Generic perms +###################################################################### + +class HasProjectPerm(PermissionComponent): + def __init__(self, perm, *components): + self.project_perm = perm + super().__init__(*components) + + def check_permissions(self, request, view, obj=None): + return services.user_has_perm(request.user, self.project_perm, obj) + + +class IsObjectOwner(PermissionComponent): + def check_permissions(self, request, view, obj=None): + if obj.owner is None: + return False + + return obj.owner == request.user + + +###################################################################### +# Project Perms +###################################################################### + +class IsProjectAdmin(PermissionComponent): + def check_permissions(self, request, view, obj=None): + return services.is_project_admin(request.user, obj) diff --git a/taiga/permissions/service.py b/taiga/permissions/services.py similarity index 96% rename from taiga/permissions/service.py rename to taiga/permissions/services.py index 02922574..9ed1e8c3 100644 --- a/taiga/permissions/service.py +++ b/taiga/permissions/services.py @@ -16,10 +16,11 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . -from .permissions import ADMINS_PERMISSIONS, MEMBERS_PERMISSIONS, ANON_PERMISSIONS +from .choices import ADMINS_PERMISSIONS, MEMBERS_PERMISSIONS, ANON_PERMISSIONS from django.apps import apps + def _get_user_project_membership(user, project, cache="user"): """ cache param determines how memberships are calculated trying to reuse the existing data @@ -83,10 +84,6 @@ def user_has_perm(user, perm, obj=None, cache="user"): return perm in get_user_project_permissions(user, project, cache=cache) -def role_has_perm(role, perm): - return perm in role.permissions - - def _get_membership_permissions(membership): if membership and membership.role and membership.role.permissions: return membership.role.permissions @@ -97,7 +94,7 @@ def get_user_project_permissions(user, project, cache="user"): """ cache param determines how memberships are calculated trying to reuse the existing data in cache - """ + """ membership = _get_user_project_membership(user, project, cache=cache) if user.is_superuser: admins_permissions = list(map(lambda perm: perm[0], ADMINS_PERMISSIONS)) diff --git a/taiga/projects/api.py b/taiga/projects/api.py index a7c3aa61..17684fd3 100644 --- a/taiga/projects/api.py +++ b/taiga/projects/api.py @@ -51,8 +51,8 @@ from taiga.projects.userstories.models import UserStory, RolePoints from taiga.projects.tasks.models import Task from taiga.projects.issues.models import Issue from taiga.projects.likes.mixins.viewsets import LikedResourceMixin, FansViewSetMixin -from taiga.permissions import service as permissions_service -from taiga.users import services as users_service +from taiga.permissions import services as permissions_services +from taiga.users import services as users_services from . import filters as project_filters from . import models @@ -147,7 +147,7 @@ class ProjectViewSet(LikedResourceMixin, HistoryResourceMixin, else: project = self.get_object() - if permissions_service.is_project_admin(self.request.user, project): + if permissions_services.is_project_admin(self.request.user, project): serializer_class = self.admin_serializer_class return serializer_class @@ -415,7 +415,7 @@ class ProjectViewSet(LikedResourceMixin, HistoryResourceMixin, update_permissions = True if update_permissions: - permissions_service.set_base_permissions_for_project(obj) + permissions_services.set_base_permissions_for_project(obj) def pre_save(self, obj): if not obj.id: @@ -603,12 +603,12 @@ class MembershipViewSet(BlockedByProjectMixin, ModelCrudViewSet): use_admin_serializer = True if self.action == "retrieve": - use_admin_serializer = permissions_service.is_project_admin(self.request.user, self.object.project) + use_admin_serializer = permissions_services.is_project_admin(self.request.user, self.object.project) project_id = self.request.QUERY_PARAMS.get("project", None) if self.action == "list" and project_id is not None: project = get_object_or_404(models.Project, pk=project_id) - use_admin_serializer = permissions_service.is_project_admin(self.request.user, project) + use_admin_serializer = permissions_services.is_project_admin(self.request.user, project) if use_admin_serializer: return self.admin_serializer_class diff --git a/taiga/projects/history/permissions.py b/taiga/projects/history/permissions.py index fdce68cd..54fc59f1 100644 --- a/taiga/projects/history/permissions.py +++ b/taiga/projects/history/permissions.py @@ -19,7 +19,7 @@ from taiga.base.api.permissions import (TaigaResourcePermission, HasProjectPerm, IsProjectAdmin, AllowAny, IsObjectOwner, PermissionComponent) -from taiga.permissions.service import is_project_admin +from taiga.permissions.services import is_project_admin from taiga.projects.history.services import get_model_from_key, get_pk_from_key diff --git a/taiga/projects/issues/permissions.py b/taiga/projects/issues/permissions.py index 291dede7..791848f9 100644 --- a/taiga/projects/issues/permissions.py +++ b/taiga/projects/issues/permissions.py @@ -16,9 +16,9 @@ # along with this program. If not, see . -from taiga.base.api.permissions import (TaigaResourcePermission, HasProjectPerm, - IsProjectAdmin, PermissionComponent, - AllowAny, IsAuthenticated, IsSuperUser) +from taiga.base.api.permissions import TaigaResourcePermission, AllowAny, IsAuthenticated, IsSuperUser +from taiga.permissions.permissions import HasProjectPerm, IsProjectAdmin + class IssuePermission(TaigaResourcePermission): @@ -40,14 +40,6 @@ class IssuePermission(TaigaResourcePermission): unwatch_perms = IsAuthenticated() & HasProjectPerm('view_issues') -class HasIssueIdUrlParam(PermissionComponent): - def check_permissions(self, request, view, obj=None): - param = view.kwargs.get('issue_id', None) - if param: - return True - return False - - class IssueVotersPermission(TaigaResourcePermission): enought_perms = IsProjectAdmin() | IsSuperUser() global_perms = None diff --git a/taiga/projects/management/commands/sample_data.py b/taiga/projects/management/commands/sample_data.py index f5c7b6ea..be43df8c 100644 --- a/taiga/projects/management/commands/sample_data.py +++ b/taiga/projects/management/commands/sample_data.py @@ -29,7 +29,7 @@ from django.contrib.contenttypes.models import ContentType from sampledatahelper.helper import SampleDataHelper from taiga.users.models import * -from taiga.permissions.permissions import ANON_PERMISSIONS +from taiga.permissions.choices import ANON_PERMISSIONS from taiga.projects.choices import BLOCKED_BY_STAFF from taiga.projects.models import * from taiga.projects.milestones.models import * diff --git a/taiga/projects/models.py b/taiga/projects/models.py index cd38c35b..506c2736 100644 --- a/taiga/projects/models.py +++ b/taiga/projects/models.py @@ -40,7 +40,7 @@ from taiga.base.utils.sequence import arithmetic_progression from taiga.base.utils.slug import slugify_uniquely from taiga.base.utils.slug import slugify_uniquely_for_queryset -from taiga.permissions.permissions import ANON_PERMISSIONS, MEMBERS_PERMISSIONS +from taiga.permissions.choices import ANON_PERMISSIONS, MEMBERS_PERMISSIONS from taiga.projects.notifications.choices import NotifyLevel from taiga.projects.notifications.services import ( @@ -366,7 +366,8 @@ class Project(ProjectDefaults, TaggedMixin, models.Model): @cached_property def cached_memberships(self): - return {m.user.id: m for m in self.memberships.exclude(user__isnull=True).select_related("user", "project", "role")} + return {m.user.id: m for m in self.memberships.exclude(user__isnull=True) + .select_related("user", "project", "role")} def cached_memberships_for_user(self, user): return self.cached_memberships.get(user.id, None) @@ -966,9 +967,11 @@ class ProjectTemplate(models.Model): project=project) if self.priorities: - project.default_priority = Priority.objects.get(name=self.default_options["priority"], project=project) + project.default_priority = Priority.objects.get(name=self.default_options["priority"], + project=project) if self.severities: - project.default_severity = Severity.objects.get(name=self.default_options["severity"], project=project) + project.default_severity = Severity.objects.get(name=self.default_options["severity"], + project=project) return project diff --git a/taiga/projects/notifications/services.py b/taiga/projects/notifications/services.py index 22d518cb..27e4b5ae 100644 --- a/taiga/projects/notifications/services.py +++ b/taiga/projects/notifications/services.py @@ -35,7 +35,7 @@ from taiga.projects.history.choices import HistoryType from taiga.projects.history.services import (make_key_from_model_object, get_last_snapshot_for_key, get_model_from_key) -from taiga.permissions.service import user_has_perm +from taiga.permissions.services import user_has_perm from .models import HistoryChangeNotification, Watched diff --git a/taiga/projects/permissions.py b/taiga/projects/permissions.py index de65cd69..ba4f3260 100644 --- a/taiga/projects/permissions.py +++ b/taiga/projects/permissions.py @@ -18,18 +18,21 @@ from django.utils.translation import ugettext as _ from taiga.base.api.permissions import TaigaResourcePermission -from taiga.base.api.permissions import HasProjectPerm from taiga.base.api.permissions import IsAuthenticated -from taiga.base.api.permissions import IsProjectAdmin from taiga.base.api.permissions import AllowAny from taiga.base.api.permissions import IsSuperUser +from taiga.base.api.permissions import IsObjectOwner from taiga.base.api.permissions import PermissionComponent from taiga.base import exceptions as exc -from taiga.projects.models import Membership +from taiga.permissions.permissions import HasProjectPerm +from taiga.permissions.permissions import IsProjectAdmin + +from . import models from . import services + class CanLeaveProject(PermissionComponent): def check_permissions(self, request, view, obj=None): if not obj or not request.user.is_authenticated(): @@ -37,20 +40,12 @@ class CanLeaveProject(PermissionComponent): try: if not services.can_user_leave_project(request.user, obj): - raise exc.PermissionDenied(_("You can't leave the project if you are the owner or there are no more admins")) + raise exc.PermissionDenied(_("You can't leave the project if you are the owner or there are " + "no more admins")) return True - except Membership.DoesNotExist: + except models.Membership.DoesNotExist: return False -class IsMainOwner(PermissionComponent): - def check_permissions(self, request, view, obj=None): - if not obj or not request.user.is_authenticated(): - return False - - if obj.owner is None: - return False - - return obj.owner == request.user class ProjectPermission(TaigaResourcePermission): retrieve_perms = HasProjectPerm('view_project') @@ -79,7 +74,7 @@ class ProjectPermission(TaigaResourcePermission): leave_perms = CanLeaveProject() transfer_validate_token_perms = IsAuthenticated() & HasProjectPerm('view_project') transfer_request_perms = IsProjectAdmin() - transfer_start_perms = IsMainOwner() + transfer_start_perms = IsObjectOwner() transfer_reject_perms = IsAuthenticated() & HasProjectPerm('view_project') transfer_accept_perms = IsAuthenticated() & HasProjectPerm('view_project') diff --git a/taiga/projects/references/api.py b/taiga/projects/references/api.py index 028d4642..0775bc72 100644 --- a/taiga/projects/references/api.py +++ b/taiga/projects/references/api.py @@ -21,7 +21,7 @@ from taiga.base import exceptions as exc from taiga.base import response from taiga.base.api import viewsets from taiga.base.api.utils import get_object_or_404 -from taiga.permissions.service import user_has_perm +from taiga.permissions.services import user_has_perm from .serializers import ResolverSerializer from . import permissions diff --git a/taiga/projects/serializers.py b/taiga/projects/serializers.py index 661aeece..7096919a 100644 --- a/taiga/projects/serializers.py +++ b/taiga/projects/serializers.py @@ -32,8 +32,8 @@ from taiga.users.serializers import UserBasicInfoSerializer from taiga.users.serializers import ProjectRoleSerializer from taiga.users.validators import RoleExistsValidator -from taiga.permissions.service import get_user_project_permissions -from taiga.permissions.service import is_project_admin, is_project_owner +from taiga.permissions.services import get_user_project_permissions +from taiga.permissions.services import is_project_admin, is_project_owner from taiga.projects.mixins.serializers import ValidateDuplicatedNameInProjectMixin from . import models diff --git a/taiga/projects/signals.py b/taiga/projects/signals.py index 51ff6485..32da1176 100644 --- a/taiga/projects/signals.py +++ b/taiga/projects/signals.py @@ -67,7 +67,6 @@ def project_post_save(sender, instance, created, **kwargs): if instance._importing: return - template = getattr(instance, "creation_template", None) if template is None: ProjectTemplate = apps.get_model("projects", "ProjectTemplate") diff --git a/taiga/projects/tasks/permissions.py b/taiga/projects/tasks/permissions.py index 9e189f10..566fc79c 100644 --- a/taiga/projects/tasks/permissions.py +++ b/taiga/projects/tasks/permissions.py @@ -15,9 +15,8 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . -from taiga.base.api.permissions import (TaigaResourcePermission, HasProjectPerm, - IsAuthenticated, IsProjectAdmin, AllowAny, - IsSuperUser) +from taiga.base.api.permissions import TaigaResourcePermission, AllowAny, IsAuthenticated, IsSuperUser +from taiga.permissions.permissions import HasProjectPerm, IsProjectAdmin class TaskPermission(TaigaResourcePermission): diff --git a/taiga/projects/userstories/api.py b/taiga/projects/userstories/api.py index 815560c1..745268cd 100644 --- a/taiga/projects/userstories/api.py +++ b/taiga/projects/userstories/api.py @@ -112,7 +112,6 @@ class UserStoryViewSet(OCCResourceMixin, VotedResourceMixin, HistoryResourceMixi return super().update(request, *args, **kwargs) - def get_queryset(self): qs = super().get_queryset() qs = qs.prefetch_related("role_points", diff --git a/taiga/projects/userstories/permissions.py b/taiga/projects/userstories/permissions.py index 326c99fe..11aa5b73 100644 --- a/taiga/projects/userstories/permissions.py +++ b/taiga/projects/userstories/permissions.py @@ -15,12 +15,13 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . -from taiga.base.api.permissions import (TaigaResourcePermission, HasProjectPerm, - IsAuthenticated, IsProjectAdmin, - AllowAny, IsSuperUser) +from taiga.base.api.permissions import TaigaResourcePermission, AllowAny, IsAuthenticated, IsSuperUser +from taiga.permissions.permissions import HasProjectPerm, IsProjectAdmin class UserStoryPermission(TaigaResourcePermission): + enought_perms = IsProjectAdmin() | IsSuperUser() + global_perms = None retrieve_perms = HasProjectPerm('view_us') create_perms = HasProjectPerm('add_us_to_project') | HasProjectPerm('add_us') update_perms = HasProjectPerm('modify_us') diff --git a/taiga/searches/api.py b/taiga/searches/api.py index be8a0e9a..f10554a8 100644 --- a/taiga/searches/api.py +++ b/taiga/searches/api.py @@ -21,7 +21,7 @@ from taiga.base.api import viewsets from taiga.base import response from taiga.base.api.utils import get_object_or_404 -from taiga.permissions.service import user_has_perm +from taiga.permissions.services import user_has_perm from . import services from . import serializers diff --git a/taiga/timeline/permissions.py b/taiga/timeline/permissions.py index b71530ed..f2753869 100644 --- a/taiga/timeline/permissions.py +++ b/taiga/timeline/permissions.py @@ -15,13 +15,17 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . -from taiga.base.api.permissions import (TaigaResourcePermission, HasProjectPerm, - AllowAny) +from taiga.base.api.permissions import TaigaResourcePermission, AllowAny, IsSuperUser +from taiga.permissions.permissions import HasProjectPerm, IsProjectAdmin class UserTimelinePermission(TaigaResourcePermission): + enought_perms = IsSuperUser() + global_perms = None retrieve_perms = AllowAny() class ProjectTimelinePermission(TaigaResourcePermission): + enought_perms = IsProjectAdmin() | IsSuperUser() + global_perms = None retrieve_perms = HasProjectPerm('view_project') diff --git a/taiga/users/models.py b/taiga/users/models.py index a0a9048b..c1cf4a3b 100644 --- a/taiga/users/models.py +++ b/taiga/users/models.py @@ -38,7 +38,7 @@ from djorm_pgarray.fields import TextArrayField from taiga.auth.tokens import get_token_for_user from taiga.base.utils.slug import slugify_uniquely from taiga.base.utils.files import get_file_path -from taiga.permissions.permissions import MEMBERS_PERMISSIONS +from taiga.permissions.choices import MEMBERS_PERMISSIONS from taiga.projects.choices import BLOCKED_BY_OWNER_LEAVING from taiga.projects.notifications.choices import NotifyLevel diff --git a/taiga/webhooks/permissions.py b/taiga/webhooks/permissions.py index bc1cae61..82ca5bd7 100644 --- a/taiga/webhooks/permissions.py +++ b/taiga/webhooks/permissions.py @@ -18,7 +18,7 @@ from taiga.base.api.permissions import (TaigaResourcePermission, IsProjectAdmin, AllowAny, PermissionComponent) -from taiga.permissions.service import is_project_admin +from taiga.permissions.services import is_project_admin class IsWebhookProjectAdmin(PermissionComponent): diff --git a/tests/factories.py b/tests/factories.py index 252ce47a..2f1ddccc 100644 --- a/tests/factories.py +++ b/tests/factories.py @@ -26,7 +26,7 @@ from .utils import DUMMY_BMP_DATA import factory -from taiga.permissions.permissions import MEMBERS_PERMISSIONS +from taiga.permissions.choices import MEMBERS_PERMISSIONS diff --git a/tests/integration/resources_permissions/test_attachment_resources.py b/tests/integration/resources_permissions/test_attachment_resources.py index c48a7e3a..357b13a7 100644 --- a/tests/integration/resources_permissions/test_attachment_resources.py +++ b/tests/integration/resources_permissions/test_attachment_resources.py @@ -4,7 +4,7 @@ from django.test.client import MULTIPART_CONTENT from taiga.base.utils import json -from taiga.permissions.permissions import MEMBERS_PERMISSIONS, ANON_PERMISSIONS +from taiga.permissions.choices import MEMBERS_PERMISSIONS, ANON_PERMISSIONS from taiga.projects import choices as project_choices from taiga.projects.attachments.serializers import AttachmentSerializer diff --git a/tests/integration/resources_permissions/test_history_resources.py b/tests/integration/resources_permissions/test_history_resources.py index 9d39b9d7..62ba4e77 100644 --- a/tests/integration/resources_permissions/test_history_resources.py +++ b/tests/integration/resources_permissions/test_history_resources.py @@ -2,7 +2,7 @@ from django.core.urlresolvers import reverse from django.utils import timezone from taiga.base.utils import json -from taiga.permissions.permissions import MEMBERS_PERMISSIONS, ANON_PERMISSIONS +from taiga.permissions.choices import MEMBERS_PERMISSIONS, ANON_PERMISSIONS from taiga.projects.history.models import HistoryEntry from taiga.projects.history.choices import HistoryType from taiga.projects.history.services import make_key_from_model_object diff --git a/tests/integration/resources_permissions/test_issues_custom_attributes_resource.py b/tests/integration/resources_permissions/test_issues_custom_attributes_resource.py index 718172df..60b57eed 100644 --- a/tests/integration/resources_permissions/test_issues_custom_attributes_resource.py +++ b/tests/integration/resources_permissions/test_issues_custom_attributes_resource.py @@ -21,7 +21,7 @@ from django.core.urlresolvers import reverse from taiga.base.utils import json from taiga.projects import choices as project_choices from taiga.projects.custom_attributes import serializers -from taiga.permissions.permissions import (MEMBERS_PERMISSIONS, +from taiga.permissions.choices import (MEMBERS_PERMISSIONS, ANON_PERMISSIONS) from tests import factories as f diff --git a/tests/integration/resources_permissions/test_issues_resources.py b/tests/integration/resources_permissions/test_issues_resources.py index 4cf3e2a1..bb532473 100644 --- a/tests/integration/resources_permissions/test_issues_resources.py +++ b/tests/integration/resources_permissions/test_issues_resources.py @@ -4,7 +4,7 @@ from django.core.urlresolvers import reverse from taiga.projects import choices as project_choices from taiga.projects.issues.serializers import IssueSerializer -from taiga.permissions.permissions import MEMBERS_PERMISSIONS, ANON_PERMISSIONS +from taiga.permissions.choices import MEMBERS_PERMISSIONS, ANON_PERMISSIONS from taiga.base.utils import json from tests import factories as f diff --git a/tests/integration/resources_permissions/test_milestones_resources.py b/tests/integration/resources_permissions/test_milestones_resources.py index c42c0580..56f072f4 100644 --- a/tests/integration/resources_permissions/test_milestones_resources.py +++ b/tests/integration/resources_permissions/test_milestones_resources.py @@ -6,7 +6,7 @@ from taiga.projects import choices as project_choices from taiga.projects.milestones.serializers import MilestoneSerializer from taiga.projects.milestones.models import Milestone from taiga.projects.notifications.services import add_watcher -from taiga.permissions.permissions import MEMBERS_PERMISSIONS, ANON_PERMISSIONS +from taiga.permissions.choices import MEMBERS_PERMISSIONS, ANON_PERMISSIONS from tests import factories as f from tests.utils import helper_test_http_method, disconnect_signals, reconnect_signals diff --git a/tests/integration/resources_permissions/test_modules_resources.py b/tests/integration/resources_permissions/test_modules_resources.py index 27269458..fc1de642 100644 --- a/tests/integration/resources_permissions/test_modules_resources.py +++ b/tests/integration/resources_permissions/test_modules_resources.py @@ -2,7 +2,7 @@ import uuid from django.core.urlresolvers import reverse -from taiga.permissions.permissions import MEMBERS_PERMISSIONS, ANON_PERMISSIONS +from taiga.permissions.choices import MEMBERS_PERMISSIONS, ANON_PERMISSIONS from taiga.base.utils import json from tests import factories as f diff --git a/tests/integration/resources_permissions/test_projects_choices_resources.py b/tests/integration/resources_permissions/test_projects_choices_resources.py index 207889f9..c26e51b5 100644 --- a/tests/integration/resources_permissions/test_projects_choices_resources.py +++ b/tests/integration/resources_permissions/test_projects_choices_resources.py @@ -4,7 +4,7 @@ from taiga.base.utils import json from taiga.projects import choices as project_choices from taiga.projects import serializers from taiga.users.serializers import RoleSerializer -from taiga.permissions.permissions import MEMBERS_PERMISSIONS +from taiga.permissions.choices import MEMBERS_PERMISSIONS from tests import factories as f from tests.utils import helper_test_http_method diff --git a/tests/integration/resources_permissions/test_projects_resource.py b/tests/integration/resources_permissions/test_projects_resource.py index 9bcd7a9f..a2ec130e 100644 --- a/tests/integration/resources_permissions/test_projects_resource.py +++ b/tests/integration/resources_permissions/test_projects_resource.py @@ -4,7 +4,7 @@ from django.apps import apps from taiga.base.utils import json from taiga.projects import choices as project_choices from taiga.projects.serializers import ProjectDetailSerializer -from taiga.permissions.permissions import MEMBERS_PERMISSIONS +from taiga.permissions.choices import MEMBERS_PERMISSIONS from tests import factories as f from tests.utils import helper_test_http_method, helper_test_http_method_and_count diff --git a/tests/integration/resources_permissions/test_resolver_resources.py b/tests/integration/resources_permissions/test_resolver_resources.py index 6858d976..c8634c46 100644 --- a/tests/integration/resources_permissions/test_resolver_resources.py +++ b/tests/integration/resources_permissions/test_resolver_resources.py @@ -1,6 +1,6 @@ from django.core.urlresolvers import reverse -from taiga.permissions.permissions import MEMBERS_PERMISSIONS, ANON_PERMISSIONS +from taiga.permissions.choices import MEMBERS_PERMISSIONS, ANON_PERMISSIONS from tests import factories as f from tests.utils import helper_test_http_method, disconnect_signals, reconnect_signals diff --git a/tests/integration/resources_permissions/test_search_resources.py b/tests/integration/resources_permissions/test_search_resources.py index 783818d3..d72e7bc2 100644 --- a/tests/integration/resources_permissions/test_search_resources.py +++ b/tests/integration/resources_permissions/test_search_resources.py @@ -1,6 +1,6 @@ from django.core.urlresolvers import reverse -from taiga.permissions.permissions import MEMBERS_PERMISSIONS, ANON_PERMISSIONS +from taiga.permissions.choices import MEMBERS_PERMISSIONS, ANON_PERMISSIONS from tests import factories as f from tests.utils import helper_test_http_method_and_keys, disconnect_signals, reconnect_signals diff --git a/tests/integration/resources_permissions/test_tasks_custom_attributes_resource.py b/tests/integration/resources_permissions/test_tasks_custom_attributes_resource.py index 44509354..2262a222 100644 --- a/tests/integration/resources_permissions/test_tasks_custom_attributes_resource.py +++ b/tests/integration/resources_permissions/test_tasks_custom_attributes_resource.py @@ -21,7 +21,7 @@ from django.core.urlresolvers import reverse from taiga.base.utils import json from taiga.projects import choices as project_choices from taiga.projects.custom_attributes import serializers -from taiga.permissions.permissions import (MEMBERS_PERMISSIONS, +from taiga.permissions.choices import (MEMBERS_PERMISSIONS, ANON_PERMISSIONS) from tests import factories as f diff --git a/tests/integration/resources_permissions/test_tasks_resources.py b/tests/integration/resources_permissions/test_tasks_resources.py index 274f7a0c..aff81389 100644 --- a/tests/integration/resources_permissions/test_tasks_resources.py +++ b/tests/integration/resources_permissions/test_tasks_resources.py @@ -5,7 +5,7 @@ from django.core.urlresolvers import reverse from taiga.base.utils import json from taiga.projects import choices as project_choices from taiga.projects.tasks.serializers import TaskSerializer -from taiga.permissions.permissions import MEMBERS_PERMISSIONS, ANON_PERMISSIONS +from taiga.permissions.choices import MEMBERS_PERMISSIONS, ANON_PERMISSIONS from taiga.projects.occ import OCCResourceMixin from tests import factories as f diff --git a/tests/integration/resources_permissions/test_timelines_resources.py b/tests/integration/resources_permissions/test_timelines_resources.py index ffc4e41d..ed68f67b 100644 --- a/tests/integration/resources_permissions/test_timelines_resources.py +++ b/tests/integration/resources_permissions/test_timelines_resources.py @@ -1,6 +1,6 @@ from django.core.urlresolvers import reverse -from taiga.permissions.permissions import MEMBERS_PERMISSIONS, ANON_PERMISSIONS +from taiga.permissions.choices import MEMBERS_PERMISSIONS, ANON_PERMISSIONS from tests import factories as f from tests.utils import helper_test_http_method, disconnect_signals, reconnect_signals diff --git a/tests/integration/resources_permissions/test_userstories_custom_attributes_resource.py b/tests/integration/resources_permissions/test_userstories_custom_attributes_resource.py index 030060f3..64e7324a 100644 --- a/tests/integration/resources_permissions/test_userstories_custom_attributes_resource.py +++ b/tests/integration/resources_permissions/test_userstories_custom_attributes_resource.py @@ -21,7 +21,7 @@ from django.core.urlresolvers import reverse from taiga.base.utils import json from taiga.projects import choices as project_choices from taiga.projects.custom_attributes import serializers -from taiga.permissions.permissions import (MEMBERS_PERMISSIONS, +from taiga.permissions.choices import (MEMBERS_PERMISSIONS, ANON_PERMISSIONS) diff --git a/tests/integration/resources_permissions/test_userstories_resources.py b/tests/integration/resources_permissions/test_userstories_resources.py index 0daadad5..4da7b081 100644 --- a/tests/integration/resources_permissions/test_userstories_resources.py +++ b/tests/integration/resources_permissions/test_userstories_resources.py @@ -5,7 +5,7 @@ from django.core.urlresolvers import reverse from taiga.base.utils import json from taiga.projects import choices as project_choices from taiga.projects.userstories.serializers import UserStorySerializer -from taiga.permissions.permissions import MEMBERS_PERMISSIONS, ANON_PERMISSIONS +from taiga.permissions.choices import MEMBERS_PERMISSIONS, ANON_PERMISSIONS from taiga.projects.occ import OCCResourceMixin from tests import factories as f diff --git a/tests/integration/resources_permissions/test_wiki_resources.py b/tests/integration/resources_permissions/test_wiki_resources.py index ed76e769..aaee4e53 100644 --- a/tests/integration/resources_permissions/test_wiki_resources.py +++ b/tests/integration/resources_permissions/test_wiki_resources.py @@ -1,7 +1,7 @@ from django.core.urlresolvers import reverse from taiga.base.utils import json -from taiga.permissions.permissions import MEMBERS_PERMISSIONS, ANON_PERMISSIONS +from taiga.permissions.choices import MEMBERS_PERMISSIONS, ANON_PERMISSIONS from taiga.projects import choices as project_choices from taiga.projects.notifications.services import add_watcher from taiga.projects.occ import OCCResourceMixin diff --git a/tests/integration/test_notifications.py b/tests/integration/test_notifications.py index bb2cdfd8..4d341086 100644 --- a/tests/integration/test_notifications.py +++ b/tests/integration/test_notifications.py @@ -41,7 +41,7 @@ from taiga.projects.history.services import take_snapshot from taiga.projects.issues.serializers import IssueSerializer from taiga.projects.userstories.serializers import UserStorySerializer from taiga.projects.tasks.serializers import TaskSerializer -from taiga.permissions.permissions import MEMBERS_PERMISSIONS +from taiga.permissions.choices import MEMBERS_PERMISSIONS pytestmark = pytest.mark.django_db diff --git a/tests/integration/test_permissions.py b/tests/integration/test_permissions.py index ddcf9e34..7ec25929 100644 --- a/tests/integration/test_permissions.py +++ b/tests/integration/test_permissions.py @@ -1,6 +1,6 @@ import pytest -from taiga.permissions import service, permissions +from taiga.permissions import services, choices from django.contrib.auth.models import AnonymousUser from .. import factories @@ -15,15 +15,15 @@ def test_get_user_project_role(): role = factories.RoleFactory() membership = factories.MembershipFactory(user=user1, project=project, role=role) - assert service._get_user_project_membership(user1, project) == membership - assert service._get_user_project_membership(user2, project) is None + assert services._get_user_project_membership(user1, project) == membership + assert services._get_user_project_membership(user2, project) is None def test_anon_get_user_project_permissions(): project = factories.ProjectFactory() project.anon_permissions = ["test1"] project.public_permissions = ["test2"] - assert service.get_user_project_permissions(AnonymousUser(), project) == set(["test1"]) + assert services.get_user_project_permissions(AnonymousUser(), project) == set(["test1"]) def test_user_get_user_project_permissions_on_public_project(): @@ -31,7 +31,7 @@ def test_user_get_user_project_permissions_on_public_project(): project = factories.ProjectFactory() project.anon_permissions = ["test1"] project.public_permissions = ["test2"] - assert service.get_user_project_permissions(user1, project) == set(["test1", "test2"]) + assert services.get_user_project_permissions(user1, project) == set(["test1", "test2"]) def test_user_get_user_project_permissions_on_private_project(): @@ -40,7 +40,7 @@ def test_user_get_user_project_permissions_on_private_project(): project.anon_permissions = ["test1"] project.public_permissions = ["test2"] project.is_private = True - assert service.get_user_project_permissions(user1, project) == set(["test1", "test2"]) + assert services.get_user_project_permissions(user1, project) == set(["test1", "test2"]) def test_owner_get_user_project_permissions(): @@ -55,7 +55,7 @@ def test_owner_get_user_project_permissions(): expected_perms = set( ["test1", "test2", "view_us"] ) - assert service.get_user_project_permissions(user1, project) == expected_perms + assert services.get_user_project_permissions(user1, project) == expected_perms def test_owner_member_get_user_project_permissions(): @@ -68,10 +68,10 @@ def test_owner_member_get_user_project_permissions(): expected_perms = set( ["test1", "test2", "test3"] + - [x[0] for x in permissions.ADMINS_PERMISSIONS] + - [x[0] for x in permissions.MEMBERS_PERMISSIONS] + [x[0] for x in choices.ADMINS_PERMISSIONS] + + [x[0] for x in choices.MEMBERS_PERMISSIONS] ) - assert service.get_user_project_permissions(user1, project) == expected_perms + assert services.get_user_project_permissions(user1, project) == expected_perms def test_member_get_user_project_permissions(): @@ -82,22 +82,22 @@ def test_member_get_user_project_permissions(): role = factories.RoleFactory(permissions=["test3"]) factories.MembershipFactory(user=user1, project=project, role=role) - assert service.get_user_project_permissions(user1, project) == set(["test1", "test2", "test3"]) + assert services.get_user_project_permissions(user1, project) == set(["test1", "test2", "test3"]) def test_anon_user_has_perm(): project = factories.ProjectFactory() project.anon_permissions = ["test"] - assert service.user_has_perm(AnonymousUser(), "test", project) is True - assert service.user_has_perm(AnonymousUser(), "fail", project) is False + assert services.user_has_perm(AnonymousUser(), "test", project) is True + assert services.user_has_perm(AnonymousUser(), "fail", project) is False def test_authenticated_user_has_perm_on_project(): user1 = factories.UserFactory() project = factories.ProjectFactory() project.public_permissions = ["test"] - assert service.user_has_perm(user1, "test", project) is True - assert service.user_has_perm(user1, "fail", project) is False + assert services.user_has_perm(user1, "test", project) is True + assert services.user_has_perm(user1, "fail", project) is False def test_authenticated_user_has_perm_on_project_related_object(): @@ -106,10 +106,10 @@ def test_authenticated_user_has_perm_on_project_related_object(): project.public_permissions = ["test"] us = factories.UserStoryFactory(project=project) - assert service.user_has_perm(user1, "test", us) is True - assert service.user_has_perm(user1, "fail", us) is False + assert services.user_has_perm(user1, "test", us) is True + assert services.user_has_perm(user1, "fail", us) is False def test_authenticated_user_has_perm_on_invalid_object(): user1 = factories.UserFactory() - assert service.user_has_perm(user1, "test", user1) is False + assert services.user_has_perm(user1, "test", user1) is False diff --git a/tests/integration/test_projects.py b/tests/integration/test_projects.py index 6111747b..28a0dbe3 100644 --- a/tests/integration/test_projects.py +++ b/tests/integration/test_projects.py @@ -7,7 +7,7 @@ from django.core import signing from taiga.base.utils import json from taiga.projects.services import stats as stats_services from taiga.projects.history.services import take_snapshot -from taiga.permissions.permissions import ANON_PERMISSIONS +from taiga.permissions.choices import ANON_PERMISSIONS from taiga.projects.models import Project from .. import factories as f diff --git a/tests/integration/test_searches.py b/tests/integration/test_searches.py index 334ad405..30c3247b 100644 --- a/tests/integration/test_searches.py +++ b/tests/integration/test_searches.py @@ -22,7 +22,7 @@ from django.core.urlresolvers import reverse from .. import factories as f -from taiga.permissions.permissions import MEMBERS_PERMISSIONS +from taiga.permissions.choices import MEMBERS_PERMISSIONS from tests.utils import disconnect_signals, reconnect_signals diff --git a/tests/integration/test_users.py b/tests/integration/test_users.py index 7ca9b867..be48c553 100644 --- a/tests/integration/test_users.py +++ b/tests/integration/test_users.py @@ -14,7 +14,7 @@ from taiga.base.utils.thumbnails import get_thumbnail_url from taiga.users import models from taiga.users.serializers import LikedObjectSerializer, VotedObjectSerializer from taiga.auth.tokens import get_token_for_user -from taiga.permissions.permissions import MEMBERS_PERMISSIONS, ANON_PERMISSIONS +from taiga.permissions.choices import MEMBERS_PERMISSIONS, ANON_PERMISSIONS from taiga.projects import choices as project_choices from taiga.users.services import get_watched_list, get_voted_list, get_liked_list from taiga.projects.notifications.choices import NotifyLevel diff --git a/tests/integration/test_watch_projects.py b/tests/integration/test_watch_projects.py index 6c4f7d41..5a77f086 100644 --- a/tests/integration/test_watch_projects.py +++ b/tests/integration/test_watch_projects.py @@ -20,7 +20,7 @@ import pytest import json from django.core.urlresolvers import reverse -from taiga.permissions.permissions import MEMBERS_PERMISSIONS, ANON_PERMISSIONS +from taiga.permissions.choices import MEMBERS_PERMISSIONS, ANON_PERMISSIONS from .. import factories as f diff --git a/tests/unit/test_permissions.py b/tests/unit/test_permissions.py deleted file mode 100644 index 5ef7a93d..00000000 --- a/tests/unit/test_permissions.py +++ /dev/null @@ -1,26 +0,0 @@ -# Copyright (C) 2014-2016 Andrey Antukh -# Copyright (C) 2014-2016 Jesús Espino -# Copyright (C) 2014-2016 David Barragán -# Copyright (C) 2014-2016 Alejandro Alonso -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -from taiga.permissions import service -from taiga.users.models import Role - - -def test_role_has_perm(): - role = Role() - role.permissions = ["test"] - assert service.role_has_perm(role, "test") - assert service.role_has_perm(role, "false") is False From 413a83808a5c1836270928707faa6bcd75b5dffe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Wed, 11 May 2016 20:50:19 +0200 Subject: [PATCH 4/4] US #4186: Permission to add comments --- CHANGELOG.md | 7 +- taiga/permissions/choices.py | 5 +- taiga/permissions/permissions.py | 37 + .../fixtures/initial_project_templates.json | 84 +- taiga/projects/issues/permissions.py | 5 +- .../migrations/0041_auto_20160519_1058.py | 21 + .../migrations/0042_auto_20160525_0911.py | 67 ++ taiga/projects/tasks/permissions.py | 6 +- taiga/projects/userstories/permissions.py | 6 +- taiga/projects/wiki/permissions.py | 6 +- .../migrations/0019_auto_20160519_1058.py | 21 + .../migrations/0020_auto_20160525_1229.py | 48 ++ .../test_issues_resources.py | 532 ++++++++---- .../test_tasks_resources.py | 466 +++++++---- .../test_userstories_resources.py | 380 ++++++--- .../test_wiki_resources.py | 786 ++++++++++++------ 16 files changed, 1720 insertions(+), 757 deletions(-) create mode 100644 taiga/projects/migrations/0041_auto_20160519_1058.py create mode 100644 taiga/projects/migrations/0042_auto_20160525_0911.py create mode 100644 taiga/users/migrations/0019_auto_20160519_1058.py create mode 100644 taiga/users/migrations/0020_auto_20160525_1229.py diff --git a/CHANGELOG.md b/CHANGELOG.md index eca3114a..97b090ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,13 @@ # Changelog # ## 2.2.0 ??? (unreleased) + ### Features -- [API] edit comment endpoint: comment owners and project admins can edit existing comments +- Now comment owners and project admins can edit existing comments with the history Entry endpoint. +- Add a new permissions to allow add comments instead of use the existent modify permission for this purpose. + +### Misc +- Lots of small and not so small bugfixes. ## 2.1.0 Ursus Americanus (2016-05-03) diff --git a/taiga/permissions/choices.py b/taiga/permissions/choices.py index bfd7192e..f617da97 100644 --- a/taiga/permissions/choices.py +++ b/taiga/permissions/choices.py @@ -27,7 +27,6 @@ ANON_PERMISSIONS = [ ('view_wiki_pages', _('View wiki pages')), ('view_wiki_links', _('View wiki links')), ] - MEMBERS_PERMISSIONS = [ ('view_project', _('View project')), # Milestone permissions @@ -39,21 +38,25 @@ MEMBERS_PERMISSIONS = [ ('view_us', _('View user story')), ('add_us', _('Add user story')), ('modify_us', _('Modify user story')), + ('comment_us', _('Comment user story')), ('delete_us', _('Delete user story')), # Task permissions ('view_tasks', _('View tasks')), ('add_task', _('Add task')), ('modify_task', _('Modify task')), + ('comment_task', _('Comment task')), ('delete_task', _('Delete task')), # Issue permissions ('view_issues', _('View issues')), ('add_issue', _('Add issue')), ('modify_issue', _('Modify issue')), + ('comment_issue', _('Comment issue')), ('delete_issue', _('Delete issue')), # Wiki page permissions ('view_wiki_pages', _('View wiki pages')), ('add_wiki_page', _('Add wiki page')), ('modify_wiki_page', _('Modify wiki page')), + ('comment_wiki_page', _('Comment wiki page')), ('delete_wiki_page', _('Delete wiki page')), # Wiki link permissions ('view_wiki_links', _('View wiki links')), diff --git a/taiga/permissions/permissions.py b/taiga/permissions/permissions.py index 4e563522..c2b7699a 100644 --- a/taiga/permissions/permissions.py +++ b/taiga/permissions/permissions.py @@ -51,3 +51,40 @@ class IsObjectOwner(PermissionComponent): class IsProjectAdmin(PermissionComponent): def check_permissions(self, request, view, obj=None): return services.is_project_admin(request.user, obj) + + +###################################################################### +# Common perms for stories, tasks and issues +###################################################################### + +class CommentAndOrUpdatePerm(PermissionComponent): + def __init__(self, update_perm, comment_perm, *components): + self.update_perm = update_perm + self.comment_perm = comment_perm + super().__init__(*components) + + def check_permissions(self, request, view, obj=None): + if not obj: + return False + + project_id = request.DATA.get('project', None) + if project_id and obj.project_id != project_id: + project = apps.get_model("projects", "Project").objects.get(pk=project_id) + else: + project = obj.project + + data_keys = request.DATA.keys() + + if (not services.user_has_perm(request.user, self.comment_perm, project) and + "comment" in data_keys): + # User can't comment but there is a comment in the request + #raise exc.PermissionDenied(_("You don't have permissions to comment this.")) + return False + + if (not services.user_has_perm(request.user, self.update_perm, project) and + len(data_keys - "comment")): + # User can't update but there is a change in the request + #raise exc.PermissionDenied(_("You don't have permissions to update this.")) + return False + + return True diff --git a/taiga/projects/fixtures/initial_project_templates.json b/taiga/projects/fixtures/initial_project_templates.json index 46d369a5..54e73bd0 100644 --- a/taiga/projects/fixtures/initial_project_templates.json +++ b/taiga/projects/fixtures/initial_project_templates.json @@ -1,56 +1,56 @@ [ { "model": "projects.projecttemplate", + "pk": 1, "fields": { - "is_issues_activated": true, - "task_statuses": "[{\"color\": \"#999999\", \"order\": 1, \"is_closed\": false, \"name\": \"New\", \"slug\": \"new\"}, {\"color\": \"#ff9900\", \"order\": 2, \"is_closed\": false, \"name\": \"In progress\", \"slug\": \"in-progress\"}, {\"color\": \"#ffcc00\", \"order\": 3, \"is_closed\": true, \"name\": \"Ready for test\", \"slug\": \"ready-for-test\"}, {\"color\": \"#669900\", \"order\": 4, \"is_closed\": true, \"name\": \"Closed\", \"slug\": \"closed\"}, {\"color\": \"#999999\", \"order\": 5, \"is_closed\": false, \"name\": \"Needs Info\", \"slug\": \"needs-info\"}]", - "is_backlog_activated": true, - "modified_date": "2014-07-25T10:02:46.479Z", - "us_statuses": "[{\"color\": \"#999999\", \"order\": 1, \"is_closed\": false, \"is_archived\": false, \"wip_limit\": null, \"name\": \"New\", \"slug\": \"new\"}, {\"color\": \"#ff8a84\", \"order\": 2, \"is_closed\": false, \"is_archived\": false, \"wip_limit\": null, \"name\": \"Ready\", \"slug\": \"ready\"}, {\"color\": \"#ff9900\", \"order\": 3, \"is_closed\": false, \"is_archived\": false, \"wip_limit\": null, \"name\": \"In progress\", \"slug\": \"in-progress\"}, {\"color\": \"#fcc000\", \"order\": 4, \"is_closed\": false, \"is_archived\": false, \"wip_limit\": null, \"name\": \"Ready for test\", \"slug\": \"ready-for-test\"}, {\"color\": \"#669900\", \"order\": 5, \"is_closed\": true, \"is_archived\": false, \"wip_limit\": null, \"name\": \"Done\", \"slug\": \"done\"}, {\"color\": \"#5c3566\", \"order\": 6, \"is_closed\": true, \"is_archived\": true, \"wip_limit\": null, \"name\": \"Archived\", \"slug\": \"archived\"}]", - "is_wiki_activated": true, - "roles": "[{\"order\": 10, \"slug\": \"ux\", \"permissions\": [\"add_issue\", \"modify_issue\", \"delete_issue\", \"view_issues\", \"add_milestone\", \"modify_milestone\", \"delete_milestone\", \"view_milestones\", \"view_project\", \"add_task\", \"modify_task\", \"delete_task\", \"view_tasks\", \"add_us\", \"modify_us\", \"delete_us\", \"view_us\", \"add_wiki_page\", \"modify_wiki_page\", \"delete_wiki_page\", \"view_wiki_pages\", \"add_wiki_link\", \"delete_wiki_link\", \"view_wiki_links\"], \"name\": \"UX\", \"computable\": true}, {\"order\": 20, \"slug\": \"design\", \"permissions\": [\"add_issue\", \"modify_issue\", \"delete_issue\", \"view_issues\", \"add_milestone\", \"modify_milestone\", \"delete_milestone\", \"view_milestones\", \"view_project\", \"add_task\", \"modify_task\", \"delete_task\", \"view_tasks\", \"add_us\", \"modify_us\", \"delete_us\", \"view_us\", \"add_wiki_page\", \"modify_wiki_page\", \"delete_wiki_page\", \"view_wiki_pages\", \"add_wiki_link\", \"delete_wiki_link\", \"view_wiki_links\"], \"name\": \"Design\", \"computable\": true}, {\"order\": 30, \"slug\": \"front\", \"permissions\": [\"add_issue\", \"modify_issue\", \"delete_issue\", \"view_issues\", \"add_milestone\", \"modify_milestone\", \"delete_milestone\", \"view_milestones\", \"view_project\", \"add_task\", \"modify_task\", \"delete_task\", \"view_tasks\", \"add_us\", \"modify_us\", \"delete_us\", \"view_us\", \"add_wiki_page\", \"modify_wiki_page\", \"delete_wiki_page\", \"view_wiki_pages\", \"add_wiki_link\", \"delete_wiki_link\", \"view_wiki_links\"], \"name\": \"Front\", \"computable\": true}, {\"order\": 40, \"slug\": \"back\", \"permissions\": [\"add_issue\", \"modify_issue\", \"delete_issue\", \"view_issues\", \"add_milestone\", \"modify_milestone\", \"delete_milestone\", \"view_milestones\", \"view_project\", \"add_task\", \"modify_task\", \"delete_task\", \"view_tasks\", \"add_us\", \"modify_us\", \"delete_us\", \"view_us\", \"add_wiki_page\", \"modify_wiki_page\", \"delete_wiki_page\", \"view_wiki_pages\", \"add_wiki_link\", \"delete_wiki_link\", \"view_wiki_links\"], \"name\": \"Back\", \"computable\": true}, {\"order\": 50, \"slug\": \"product-owner\", \"permissions\": [\"add_issue\", \"modify_issue\", \"delete_issue\", \"view_issues\", \"add_milestone\", \"modify_milestone\", \"delete_milestone\", \"view_milestones\", \"view_project\", \"add_task\", \"modify_task\", \"delete_task\", \"view_tasks\", \"add_us\", \"modify_us\", \"delete_us\", \"view_us\", \"add_wiki_page\", \"modify_wiki_page\", \"delete_wiki_page\", \"view_wiki_pages\", \"add_wiki_link\", \"delete_wiki_link\", \"view_wiki_links\"], \"name\": \"Product Owner\", \"computable\": false}, {\"order\": 60, \"slug\": \"stakeholder\", \"permissions\": [\"add_issue\", \"modify_issue\", \"delete_issue\", \"view_issues\", \"view_milestones\", \"view_project\", \"view_tasks\", \"view_us\", \"modify_wiki_page\", \"view_wiki_pages\", \"add_wiki_link\", \"delete_wiki_link\", \"view_wiki_links\"], \"name\": \"Stakeholder\", \"computable\": false}]", - "points": "[{\"value\": null, \"order\": 1, \"name\": \"?\"}, {\"value\": 0.0, \"order\": 2, \"name\": \"0\"}, {\"value\": 0.5, \"order\": 3, \"name\": \"1/2\"}, {\"value\": 1.0, \"order\": 4, \"name\": \"1\"}, {\"value\": 2.0, \"order\": 5, \"name\": \"2\"}, {\"value\": 3.0, \"order\": 6, \"name\": \"3\"}, {\"value\": 5.0, \"order\": 7, \"name\": \"5\"}, {\"value\": 8.0, \"order\": 8, \"name\": \"8\"}, {\"value\": 10.0, \"order\": 9, \"name\": \"10\"}, {\"value\": 13.0, \"order\": 10, \"name\": \"13\"}, {\"value\": 20.0, \"order\": 11, \"name\": \"20\"}, {\"value\": 40.0, \"order\": 12, \"name\": \"40\"}]", - "severities": "[{\"color\": \"#666666\", \"order\": 1, \"name\": \"Wishlist\"}, {\"color\": \"#669933\", \"order\": 2, \"name\": \"Minor\"}, {\"color\": \"#0000FF\", \"order\": 3, \"name\": \"Normal\"}, {\"color\": \"#FFA500\", \"order\": 4, \"name\": \"Important\"}, {\"color\": \"#CC0000\", \"order\": 5, \"name\": \"Critical\"}]", - "is_kanban_activated": false, - "priorities": "[{\"color\": \"#666666\", \"order\": 1, \"name\": \"Low\"}, {\"color\": \"#669933\", \"order\": 3, \"name\": \"Normal\"}, {\"color\": \"#CC0000\", \"order\": 5, \"name\": \"High\"}]", - "created_date": "2014-04-22T14:48:43.596Z", - "default_options": "{\"us_status\": \"New\", \"task_status\": \"New\", \"priority\": \"Normal\", \"issue_type\": \"Bug\", \"severity\": \"Normal\", \"points\": \"?\", \"issue_status\": \"New\"}", + "name": "Scrum", "slug": "scrum", - "videoconferences_extra_data": "", - "issue_statuses": "[{\"color\": \"#8C2318\", \"order\": 1, \"is_closed\": false, \"name\": \"New\", \"slug\": \"new\"}, {\"color\": \"#5E8C6A\", \"order\": 2, \"is_closed\": false, \"name\": \"In progress\", \"slug\": \"in-progress\"}, {\"color\": \"#88A65E\", \"order\": 3, \"is_closed\": true, \"name\": \"Ready for test\", \"slug\": \"ready-for-test\"}, {\"color\": \"#BFB35A\", \"order\": 4, \"is_closed\": true, \"name\": \"Closed\", \"slug\": \"closed\"}, {\"color\": \"#89BAB4\", \"order\": 5, \"is_closed\": false, \"name\": \"Needs Info\", \"slug\": \"needs-info\"}, {\"color\": \"#CC0000\", \"order\": 6, \"is_closed\": true, \"name\": \"Rejected\", \"slug\": \"rejected\"}, {\"color\": \"#666666\", \"order\": 7, \"is_closed\": false, \"name\": \"Postponed\", \"slug\": \"posponed\"}]", - "default_owner_role": "product-owner", - "issue_types": "[{\"color\": \"#89BAB4\", \"order\": 1, \"name\": \"Bug\"}, {\"color\": \"#ba89a8\", \"order\": 2, \"name\": \"Question\"}, {\"color\": \"#89a8ba\", \"order\": 3, \"name\": \"Enhancement\"}]", - "videoconferences": null, "description": "The agile product backlog in Scrum is a prioritized features list, containing short descriptions of all functionality desired in the product. When applying Scrum, it's not necessary to start a project with a lengthy, upfront effort to document all requirements. The Scrum product backlog is then allowed to grow and change as more is learned about the product and its customers", - "name": "Scrum" - }, - "pk": 1 + "created_date": "2014-04-22T14:48:43.596Z", + "modified_date": "2014-07-25T10:02:46.479Z", + "default_owner_role": "product-owner", + "is_backlog_activated": true, + "is_kanban_activated": false, + "is_wiki_activated": true, + "is_issues_activated": true, + "videoconferences": null, + "videoconferences_extra_data": "", + "default_options": "{\"severity\": \"Normal\", \"priority\": \"Normal\", \"task_status\": \"New\", \"points\": \"?\", \"us_status\": \"New\", \"issue_type\": \"Bug\", \"issue_status\": \"New\"}", + "us_statuses": "[{\"is_archived\": false, \"slug\": \"new\", \"is_closed\": false, \"wip_limit\": null, \"order\": 1, \"name\": \"New\", \"color\": \"#999999\"}, {\"is_archived\": false, \"slug\": \"ready\", \"is_closed\": false, \"wip_limit\": null, \"order\": 2, \"name\": \"Ready\", \"color\": \"#ff8a84\"}, {\"is_archived\": false, \"slug\": \"in-progress\", \"is_closed\": false, \"wip_limit\": null, \"order\": 3, \"name\": \"In progress\", \"color\": \"#ff9900\"}, {\"is_archived\": false, \"slug\": \"ready-for-test\", \"is_closed\": false, \"wip_limit\": null, \"order\": 4, \"name\": \"Ready for test\", \"color\": \"#fcc000\"}, {\"is_archived\": false, \"slug\": \"done\", \"is_closed\": true, \"wip_limit\": null, \"order\": 5, \"name\": \"Done\", \"color\": \"#669900\"}, {\"is_archived\": true, \"slug\": \"archived\", \"is_closed\": true, \"wip_limit\": null, \"order\": 6, \"name\": \"Archived\", \"color\": \"#5c3566\"}]", + "points": "[{\"order\": 1, \"name\": \"?\", \"value\": null}, {\"order\": 2, \"name\": \"0\", \"value\": 0.0}, {\"order\": 3, \"name\": \"1/2\", \"value\": 0.5}, {\"order\": 4, \"name\": \"1\", \"value\": 1.0}, {\"order\": 5, \"name\": \"2\", \"value\": 2.0}, {\"order\": 6, \"name\": \"3\", \"value\": 3.0}, {\"order\": 7, \"name\": \"5\", \"value\": 5.0}, {\"order\": 8, \"name\": \"8\", \"value\": 8.0}, {\"order\": 9, \"name\": \"10\", \"value\": 10.0}, {\"order\": 10, \"name\": \"13\", \"value\": 13.0}, {\"order\": 11, \"name\": \"20\", \"value\": 20.0}, {\"order\": 12, \"name\": \"40\", \"value\": 40.0}]", + "task_statuses": "[{\"order\": 1, \"name\": \"New\", \"slug\": \"new\", \"color\": \"#999999\", \"is_closed\": false}, {\"order\": 2, \"name\": \"In progress\", \"slug\": \"in-progress\", \"color\": \"#ff9900\", \"is_closed\": false}, {\"order\": 3, \"name\": \"Ready for test\", \"slug\": \"ready-for-test\", \"color\": \"#ffcc00\", \"is_closed\": true}, {\"order\": 4, \"name\": \"Closed\", \"slug\": \"closed\", \"color\": \"#669900\", \"is_closed\": true}, {\"order\": 5, \"name\": \"Needs Info\", \"slug\": \"needs-info\", \"color\": \"#999999\", \"is_closed\": false}]", + "issue_statuses": "[{\"order\": 1, \"name\": \"New\", \"slug\": \"new\", \"color\": \"#8C2318\", \"is_closed\": false}, {\"order\": 2, \"name\": \"In progress\", \"slug\": \"in-progress\", \"color\": \"#5E8C6A\", \"is_closed\": false}, {\"order\": 3, \"name\": \"Ready for test\", \"slug\": \"ready-for-test\", \"color\": \"#88A65E\", \"is_closed\": true}, {\"order\": 4, \"name\": \"Closed\", \"slug\": \"closed\", \"color\": \"#BFB35A\", \"is_closed\": true}, {\"order\": 5, \"name\": \"Needs Info\", \"slug\": \"needs-info\", \"color\": \"#89BAB4\", \"is_closed\": false}, {\"order\": 6, \"name\": \"Rejected\", \"slug\": \"rejected\", \"color\": \"#CC0000\", \"is_closed\": true}, {\"order\": 7, \"name\": \"Postponed\", \"slug\": \"posponed\", \"color\": \"#666666\", \"is_closed\": false}]", + "issue_types": "[{\"order\": 1, \"name\": \"Bug\", \"color\": \"#89BAB4\"}, {\"order\": 2, \"name\": \"Question\", \"color\": \"#ba89a8\"}, {\"order\": 3, \"name\": \"Enhancement\", \"color\": \"#89a8ba\"}]", + "priorities": "[{\"order\": 1, \"name\": \"Low\", \"color\": \"#666666\"}, {\"order\": 3, \"name\": \"Normal\", \"color\": \"#669933\"}, {\"order\": 5, \"name\": \"High\", \"color\": \"#CC0000\"}]", + "severities": "[{\"order\": 1, \"name\": \"Wishlist\", \"color\": \"#666666\"}, {\"order\": 2, \"name\": \"Minor\", \"color\": \"#669933\"}, {\"order\": 3, \"name\": \"Normal\", \"color\": \"#0000FF\"}, {\"order\": 4, \"name\": \"Important\", \"color\": \"#FFA500\"}, {\"order\": 5, \"name\": \"Critical\", \"color\": \"#CC0000\"}]", + "roles": "[{\"order\": 10, \"name\": \"UX\", \"slug\": \"ux\", \"computable\": true, \"permissions\": [\"add_issue\", \"modify_issue\", \"delete_issue\", \"view_issues\", \"add_milestone\", \"modify_milestone\", \"delete_milestone\", \"view_milestones\", \"view_project\", \"add_task\", \"modify_task\", \"delete_task\", \"view_tasks\", \"add_us\", \"modify_us\", \"delete_us\", \"view_us\", \"add_wiki_page\", \"modify_wiki_page\", \"delete_wiki_page\", \"view_wiki_pages\", \"add_wiki_link\", \"delete_wiki_link\", \"view_wiki_links\"]}, {\"order\": 20, \"name\": \"Design\", \"slug\": \"design\", \"computable\": true, \"permissions\": [\"add_issue\", \"modify_issue\", \"delete_issue\", \"view_issues\", \"add_milestone\", \"modify_milestone\", \"delete_milestone\", \"view_milestones\", \"view_project\", \"add_task\", \"modify_task\", \"delete_task\", \"view_tasks\", \"add_us\", \"modify_us\", \"delete_us\", \"view_us\", \"add_wiki_page\", \"modify_wiki_page\", \"delete_wiki_page\", \"view_wiki_pages\", \"add_wiki_link\", \"delete_wiki_link\", \"view_wiki_links\"]}, {\"order\": 30, \"name\": \"Front\", \"slug\": \"front\", \"computable\": true, \"permissions\": [\"add_issue\", \"modify_issue\", \"delete_issue\", \"view_issues\", \"add_milestone\", \"modify_milestone\", \"delete_milestone\", \"view_milestones\", \"view_project\", \"add_task\", \"modify_task\", \"delete_task\", \"view_tasks\", \"add_us\", \"modify_us\", \"delete_us\", \"view_us\", \"add_wiki_page\", \"modify_wiki_page\", \"delete_wiki_page\", \"view_wiki_pages\", \"add_wiki_link\", \"delete_wiki_link\", \"view_wiki_links\"]}, {\"order\": 40, \"name\": \"Back\", \"slug\": \"back\", \"computable\": true, \"permissions\": [\"add_issue\", \"modify_issue\", \"delete_issue\", \"view_issues\", \"add_milestone\", \"modify_milestone\", \"delete_milestone\", \"view_milestones\", \"view_project\", \"add_task\", \"modify_task\", \"delete_task\", \"view_tasks\", \"add_us\", \"modify_us\", \"delete_us\", \"view_us\", \"add_wiki_page\", \"modify_wiki_page\", \"delete_wiki_page\", \"view_wiki_pages\", \"add_wiki_link\", \"delete_wiki_link\", \"view_wiki_links\"]}, {\"order\": 50, \"name\": \"Product Owner\", \"slug\": \"product-owner\", \"computable\": false, \"permissions\": [\"add_issue\", \"modify_issue\", \"delete_issue\", \"view_issues\", \"add_milestone\", \"modify_milestone\", \"delete_milestone\", \"view_milestones\", \"view_project\", \"add_task\", \"modify_task\", \"delete_task\", \"view_tasks\", \"add_us\", \"modify_us\", \"delete_us\", \"view_us\", \"add_wiki_page\", \"modify_wiki_page\", \"delete_wiki_page\", \"view_wiki_pages\", \"add_wiki_link\", \"delete_wiki_link\", \"view_wiki_links\"]}, {\"order\": 60, \"name\": \"Stakeholder\", \"slug\": \"stakeholder\", \"computable\": false, \"permissions\": [\"add_issue\", \"modify_issue\", \"delete_issue\", \"view_issues\", \"view_milestones\", \"view_project\", \"view_tasks\", \"view_us\", \"modify_wiki_page\", \"view_wiki_pages\", \"add_wiki_link\", \"delete_wiki_link\", \"view_wiki_links\"]}]" + } }, { "model": "projects.projecttemplate", + "pk": 2, "fields": { - "is_issues_activated": false, - "task_statuses": "[{\"color\": \"#999999\", \"order\": 1, \"is_closed\": false, \"name\": \"New\", \"slug\": \"new\"}, {\"color\": \"#729fcf\", \"order\": 2, \"is_closed\": false, \"name\": \"In progress\", \"slug\": \"in-progress\"}, {\"color\": \"#f57900\", \"order\": 3, \"is_closed\": true, \"name\": \"Ready for test\", \"slug\": \"ready-for-test\"}, {\"color\": \"#4e9a06\", \"order\": 4, \"is_closed\": true, \"name\": \"Closed\", \"slug\": \"closed\"}, {\"color\": \"#cc0000\", \"order\": 5, \"is_closed\": false, \"name\": \"Needs Info\", \"slug\": \"needs-info\"}]", - "is_backlog_activated": false, - "modified_date": "2014-07-25T13:11:42.754Z", - "us_statuses": "[{\"wip_limit\": null, \"order\": 1, \"is_closed\": false, \"is_archived\": false, \"color\": \"#999999\", \"name\": \"New\", \"slug\": \"new\"}, {\"wip_limit\": null, \"order\": 2, \"is_closed\": false, \"is_archived\": false, \"color\": \"#f57900\", \"name\": \"Ready\", \"slug\": \"ready\"}, {\"wip_limit\": null, \"order\": 3, \"is_closed\": false, \"is_archived\": false, \"color\": \"#729fcf\", \"name\": \"In progress\", \"slug\": \"in-progress\"}, {\"wip_limit\": null, \"order\": 4, \"is_closed\": false, \"is_archived\": false, \"color\": \"#4e9a06\", \"name\": \"Ready for test\", \"slug\": \"ready-for-test\"}, {\"wip_limit\": null, \"order\": 5, \"is_closed\": true, \"is_archived\": false, \"color\": \"#cc0000\", \"name\": \"Done\", \"slug\": \"done\"}, {\"wip_limit\": null, \"order\": 6, \"is_closed\": true, \"is_archived\": true, \"color\": \"#5c3566\", \"name\": \"Archived\", \"slug\": \"archived\"}]", - "is_wiki_activated": false, - "roles": "[{\"order\": 10, \"slug\": \"ux\", \"permissions\": [\"add_issue\", \"modify_issue\", \"delete_issue\", \"view_issues\", \"add_milestone\", \"modify_milestone\", \"delete_milestone\", \"view_milestones\", \"view_project\", \"add_task\", \"modify_task\", \"delete_task\", \"view_tasks\", \"add_us\", \"modify_us\", \"delete_us\", \"view_us\", \"add_wiki_page\", \"modify_wiki_page\", \"delete_wiki_page\", \"view_wiki_pages\", \"add_wiki_link\", \"delete_wiki_link\", \"view_wiki_links\"], \"name\": \"UX\", \"computable\": true}, {\"order\": 20, \"slug\": \"design\", \"permissions\": [\"add_issue\", \"modify_issue\", \"delete_issue\", \"view_issues\", \"add_milestone\", \"modify_milestone\", \"delete_milestone\", \"view_milestones\", \"view_project\", \"add_task\", \"modify_task\", \"delete_task\", \"view_tasks\", \"add_us\", \"modify_us\", \"delete_us\", \"view_us\", \"add_wiki_page\", \"modify_wiki_page\", \"delete_wiki_page\", \"view_wiki_pages\", \"add_wiki_link\", \"delete_wiki_link\", \"view_wiki_links\"], \"name\": \"Design\", \"computable\": true}, {\"order\": 30, \"slug\": \"front\", \"permissions\": [\"add_issue\", \"modify_issue\", \"delete_issue\", \"view_issues\", \"add_milestone\", \"modify_milestone\", \"delete_milestone\", \"view_milestones\", \"view_project\", \"add_task\", \"modify_task\", \"delete_task\", \"view_tasks\", \"add_us\", \"modify_us\", \"delete_us\", \"view_us\", \"add_wiki_page\", \"modify_wiki_page\", \"delete_wiki_page\", \"view_wiki_pages\", \"add_wiki_link\", \"delete_wiki_link\", \"view_wiki_links\"], \"name\": \"Front\", \"computable\": true}, {\"order\": 40, \"slug\": \"back\", \"permissions\": [\"add_issue\", \"modify_issue\", \"delete_issue\", \"view_issues\", \"add_milestone\", \"modify_milestone\", \"delete_milestone\", \"view_milestones\", \"view_project\", \"add_task\", \"modify_task\", \"delete_task\", \"view_tasks\", \"add_us\", \"modify_us\", \"delete_us\", \"view_us\", \"add_wiki_page\", \"modify_wiki_page\", \"delete_wiki_page\", \"view_wiki_pages\", \"add_wiki_link\", \"delete_wiki_link\", \"view_wiki_links\"], \"name\": \"Back\", \"computable\": true}, {\"order\": 50, \"slug\": \"product-owner\", \"permissions\": [\"add_issue\", \"modify_issue\", \"delete_issue\", \"view_issues\", \"add_milestone\", \"modify_milestone\", \"delete_milestone\", \"view_milestones\", \"view_project\", \"add_task\", \"modify_task\", \"delete_task\", \"view_tasks\", \"add_us\", \"modify_us\", \"delete_us\", \"view_us\", \"add_wiki_page\", \"modify_wiki_page\", \"delete_wiki_page\", \"view_wiki_pages\", \"add_wiki_link\", \"delete_wiki_link\", \"view_wiki_links\"], \"name\": \"Product Owner\", \"computable\": false}, {\"order\": 60, \"slug\": \"stakeholder\", \"permissions\": [\"add_issue\", \"modify_issue\", \"delete_issue\", \"view_issues\", \"view_milestones\", \"view_project\", \"view_tasks\", \"view_us\", \"modify_wiki_page\", \"view_wiki_pages\", \"add_wiki_link\", \"delete_wiki_link\", \"view_wiki_links\"], \"name\": \"Stakeholder\", \"computable\": false}]", - "points": "[{\"value\": null, \"name\": \"?\", \"order\": 1}, {\"value\": 0.0, \"name\": \"0\", \"order\": 2}, {\"value\": 0.5, \"name\": \"1/2\", \"order\": 3}, {\"value\": 1.0, \"name\": \"1\", \"order\": 4}, {\"value\": 2.0, \"name\": \"2\", \"order\": 5}, {\"value\": 3.0, \"name\": \"3\", \"order\": 6}, {\"value\": 5.0, \"name\": \"5\", \"order\": 7}, {\"value\": 8.0, \"name\": \"8\", \"order\": 8}, {\"value\": 10.0, \"name\": \"10\", \"order\": 9}, {\"value\": 13.0, \"name\": \"13\", \"order\": 10}, {\"value\": 20.0, \"name\": \"20\", \"order\": 11}, {\"value\": 40.0, \"name\": \"40\", \"order\": 12}]", - "severities": "[{\"color\": \"#999999\", \"order\": 1, \"name\": \"Wishlist\"}, {\"color\": \"#729fcf\", \"order\": 2, \"name\": \"Minor\"}, {\"color\": \"#4e9a06\", \"order\": 3, \"name\": \"Normal\"}, {\"color\": \"#f57900\", \"order\": 4, \"name\": \"Important\"}, {\"color\": \"#CC0000\", \"order\": 5, \"name\": \"Critical\"}]", - "is_kanban_activated": true, - "priorities": "[{\"color\": \"#999999\", \"order\": 1, \"name\": \"Low\"}, {\"color\": \"#4e9a06\", \"order\": 3, \"name\": \"Normal\"}, {\"color\": \"#CC0000\", \"order\": 5, \"name\": \"High\"}]", - "created_date": "2014-04-22T14:50:19.738Z", - "default_options": "{\"us_status\": \"New\", \"task_status\": \"New\", \"priority\": \"Normal\", \"issue_type\": \"Bug\", \"severity\": \"Normal\", \"points\": \"?\", \"issue_status\": \"New\"}", + "name": "Kanban", "slug": "kanban", - "videoconferences_extra_data": "", - "issue_statuses": "[{\"color\": \"#999999\", \"order\": 1, \"is_closed\": false, \"name\": \"New\", \"slug\": \"new\"}, {\"color\": \"#729fcf\", \"order\": 2, \"is_closed\": false, \"name\": \"In progress\", \"slug\": \"in-progress\"}, {\"color\": \"#f57900\", \"order\": 3, \"is_closed\": true, \"name\": \"Ready for test\", \"slug\": \"ready-for-test\"}, {\"color\": \"#4e9a06\", \"order\": 4, \"is_closed\": true, \"name\": \"Closed\", \"slug\": \"closed\"}, {\"color\": \"#cc0000\", \"order\": 5, \"is_closed\": false, \"name\": \"Needs Info\", \"slug\": \"needs-info\"}, {\"color\": \"#d3d7cf\", \"order\": 6, \"is_closed\": true, \"name\": \"Rejected\", \"slug\": \"rejected\"}, {\"color\": \"#75507b\", \"order\": 7, \"is_closed\": false, \"name\": \"Postponed\", \"slug\": \"posponed\"}]", - "default_owner_role": "product-owner", - "issue_types": "[{\"color\": \"#cc0000\", \"order\": 1, \"name\": \"Bug\"}, {\"color\": \"#729fcf\", \"order\": 2, \"name\": \"Question\"}, {\"color\": \"#4e9a06\", \"order\": 3, \"name\": \"Enhancement\"}]", - "videoconferences": null, "description": "Kanban is a method for managing knowledge work with an emphasis on just-in-time delivery while not overloading the team members. In this approach, the process, from definition of a task to its delivery to the customer, is displayed for participants to see and team members pull work from a queue.", - "name": "Kanban" - }, - "pk": 2 + "created_date": "2014-04-22T14:50:19.738Z", + "modified_date": "2014-07-25T13:11:42.754Z", + "default_owner_role": "product-owner", + "is_backlog_activated": false, + "is_kanban_activated": true, + "is_wiki_activated": false, + "is_issues_activated": false, + "videoconferences": null, + "videoconferences_extra_data": "", + "default_options": "{\"severity\": \"Normal\", \"priority\": \"Normal\", \"task_status\": \"New\", \"points\": \"?\", \"us_status\": \"New\", \"issue_type\": \"Bug\", \"issue_status\": \"New\"}", + "us_statuses": "[{\"is_archived\": false, \"slug\": \"new\", \"is_closed\": false, \"wip_limit\": null, \"order\": 1, \"name\": \"New\", \"color\": \"#999999\"}, {\"is_archived\": false, \"slug\": \"ready\", \"is_closed\": false, \"wip_limit\": null, \"order\": 2, \"name\": \"Ready\", \"color\": \"#f57900\"}, {\"is_archived\": false, \"slug\": \"in-progress\", \"is_closed\": false, \"wip_limit\": null, \"order\": 3, \"name\": \"In progress\", \"color\": \"#729fcf\"}, {\"is_archived\": false, \"slug\": \"ready-for-test\", \"is_closed\": false, \"wip_limit\": null, \"order\": 4, \"name\": \"Ready for test\", \"color\": \"#4e9a06\"}, {\"is_archived\": false, \"slug\": \"done\", \"is_closed\": true, \"wip_limit\": null, \"order\": 5, \"name\": \"Done\", \"color\": \"#cc0000\"}, {\"is_archived\": true, \"slug\": \"archived\", \"is_closed\": true, \"wip_limit\": null, \"order\": 6, \"name\": \"Archived\", \"color\": \"#5c3566\"}]", + "points": "[{\"order\": 1, \"name\": \"?\", \"value\": null}, {\"order\": 2, \"name\": \"0\", \"value\": 0.0}, {\"order\": 3, \"name\": \"1/2\", \"value\": 0.5}, {\"order\": 4, \"name\": \"1\", \"value\": 1.0}, {\"order\": 5, \"name\": \"2\", \"value\": 2.0}, {\"order\": 6, \"name\": \"3\", \"value\": 3.0}, {\"order\": 7, \"name\": \"5\", \"value\": 5.0}, {\"order\": 8, \"name\": \"8\", \"value\": 8.0}, {\"order\": 9, \"name\": \"10\", \"value\": 10.0}, {\"order\": 10, \"name\": \"13\", \"value\": 13.0}, {\"order\": 11, \"name\": \"20\", \"value\": 20.0}, {\"order\": 12, \"name\": \"40\", \"value\": 40.0}]", + "task_statuses": "[{\"order\": 1, \"name\": \"New\", \"slug\": \"new\", \"color\": \"#999999\", \"is_closed\": false}, {\"order\": 2, \"name\": \"In progress\", \"slug\": \"in-progress\", \"color\": \"#729fcf\", \"is_closed\": false}, {\"order\": 3, \"name\": \"Ready for test\", \"slug\": \"ready-for-test\", \"color\": \"#f57900\", \"is_closed\": true}, {\"order\": 4, \"name\": \"Closed\", \"slug\": \"closed\", \"color\": \"#4e9a06\", \"is_closed\": true}, {\"order\": 5, \"name\": \"Needs Info\", \"slug\": \"needs-info\", \"color\": \"#cc0000\", \"is_closed\": false}]", + "issue_statuses": "[{\"order\": 1, \"name\": \"New\", \"slug\": \"new\", \"color\": \"#999999\", \"is_closed\": false}, {\"order\": 2, \"name\": \"In progress\", \"slug\": \"in-progress\", \"color\": \"#729fcf\", \"is_closed\": false}, {\"order\": 3, \"name\": \"Ready for test\", \"slug\": \"ready-for-test\", \"color\": \"#f57900\", \"is_closed\": true}, {\"order\": 4, \"name\": \"Closed\", \"slug\": \"closed\", \"color\": \"#4e9a06\", \"is_closed\": true}, {\"order\": 5, \"name\": \"Needs Info\", \"slug\": \"needs-info\", \"color\": \"#cc0000\", \"is_closed\": false}, {\"order\": 6, \"name\": \"Rejected\", \"slug\": \"rejected\", \"color\": \"#d3d7cf\", \"is_closed\": true}, {\"order\": 7, \"name\": \"Postponed\", \"slug\": \"posponed\", \"color\": \"#75507b\", \"is_closed\": false}]", + "issue_types": "[{\"order\": 1, \"name\": \"Bug\", \"color\": \"#cc0000\"}, {\"order\": 2, \"name\": \"Question\", \"color\": \"#729fcf\"}, {\"order\": 3, \"name\": \"Enhancement\", \"color\": \"#4e9a06\"}]", + "priorities": "[{\"order\": 1, \"name\": \"Low\", \"color\": \"#999999\"}, {\"order\": 3, \"name\": \"Normal\", \"color\": \"#4e9a06\"}, {\"order\": 5, \"name\": \"High\", \"color\": \"#CC0000\"}]", + "severities": "[{\"order\": 1, \"name\": \"Wishlist\", \"color\": \"#999999\"}, {\"order\": 2, \"name\": \"Minor\", \"color\": \"#729fcf\"}, {\"order\": 3, \"name\": \"Normal\", \"color\": \"#4e9a06\"}, {\"order\": 4, \"name\": \"Important\", \"color\": \"#f57900\"}, {\"order\": 5, \"name\": \"Critical\", \"color\": \"#CC0000\"}]", + "roles": "[{\"order\": 10, \"name\": \"UX\", \"slug\": \"ux\", \"computable\": true, \"permissions\": [\"add_issue\", \"modify_issue\", \"delete_issue\", \"view_issues\", \"add_milestone\", \"modify_milestone\", \"delete_milestone\", \"view_milestones\", \"view_project\", \"add_task\", \"modify_task\", \"delete_task\", \"view_tasks\", \"add_us\", \"modify_us\", \"delete_us\", \"view_us\", \"add_wiki_page\", \"modify_wiki_page\", \"delete_wiki_page\", \"view_wiki_pages\", \"add_wiki_link\", \"delete_wiki_link\", \"view_wiki_links\"]}, {\"order\": 20, \"name\": \"Design\", \"slug\": \"design\", \"computable\": true, \"permissions\": [\"add_issue\", \"modify_issue\", \"delete_issue\", \"view_issues\", \"add_milestone\", \"modify_milestone\", \"delete_milestone\", \"view_milestones\", \"view_project\", \"add_task\", \"modify_task\", \"delete_task\", \"view_tasks\", \"add_us\", \"modify_us\", \"delete_us\", \"view_us\", \"add_wiki_page\", \"modify_wiki_page\", \"delete_wiki_page\", \"view_wiki_pages\", \"add_wiki_link\", \"delete_wiki_link\", \"view_wiki_links\"]}, {\"order\": 30, \"name\": \"Front\", \"slug\": \"front\", \"computable\": true, \"permissions\": [\"add_issue\", \"modify_issue\", \"delete_issue\", \"view_issues\", \"add_milestone\", \"modify_milestone\", \"delete_milestone\", \"view_milestones\", \"view_project\", \"add_task\", \"modify_task\", \"delete_task\", \"view_tasks\", \"add_us\", \"modify_us\", \"delete_us\", \"view_us\", \"add_wiki_page\", \"modify_wiki_page\", \"delete_wiki_page\", \"view_wiki_pages\", \"add_wiki_link\", \"delete_wiki_link\", \"view_wiki_links\"]}, {\"order\": 40, \"name\": \"Back\", \"slug\": \"back\", \"computable\": true, \"permissions\": [\"add_issue\", \"modify_issue\", \"delete_issue\", \"view_issues\", \"add_milestone\", \"modify_milestone\", \"delete_milestone\", \"view_milestones\", \"view_project\", \"add_task\", \"modify_task\", \"delete_task\", \"view_tasks\", \"add_us\", \"modify_us\", \"delete_us\", \"view_us\", \"add_wiki_page\", \"modify_wiki_page\", \"delete_wiki_page\", \"view_wiki_pages\", \"add_wiki_link\", \"delete_wiki_link\", \"view_wiki_links\"]}, {\"order\": 50, \"name\": \"Product Owner\", \"slug\": \"product-owner\", \"computable\": false, \"permissions\": [\"add_issue\", \"modify_issue\", \"delete_issue\", \"view_issues\", \"add_milestone\", \"modify_milestone\", \"delete_milestone\", \"view_milestones\", \"view_project\", \"add_task\", \"modify_task\", \"delete_task\", \"view_tasks\", \"add_us\", \"modify_us\", \"delete_us\", \"view_us\", \"add_wiki_page\", \"modify_wiki_page\", \"delete_wiki_page\", \"view_wiki_pages\", \"add_wiki_link\", \"delete_wiki_link\", \"view_wiki_links\"]}, {\"order\": 60, \"name\": \"Stakeholder\", \"slug\": \"stakeholder\", \"computable\": false, \"permissions\": [\"add_issue\", \"modify_issue\", \"delete_issue\", \"view_issues\", \"view_milestones\", \"view_project\", \"view_tasks\", \"view_us\", \"modify_wiki_page\", \"view_wiki_pages\", \"add_wiki_link\", \"delete_wiki_link\", \"view_wiki_links\"]}]" + } } ] diff --git a/taiga/projects/issues/permissions.py b/taiga/projects/issues/permissions.py index 791848f9..ea823fcc 100644 --- a/taiga/projects/issues/permissions.py +++ b/taiga/projects/issues/permissions.py @@ -19,6 +19,7 @@ from taiga.base.api.permissions import TaigaResourcePermission, AllowAny, IsAuthenticated, IsSuperUser from taiga.permissions.permissions import HasProjectPerm, IsProjectAdmin +from taiga.permissions.permissions import CommentAndOrUpdatePerm class IssuePermission(TaigaResourcePermission): @@ -26,8 +27,8 @@ class IssuePermission(TaigaResourcePermission): global_perms = None retrieve_perms = HasProjectPerm('view_issues') create_perms = HasProjectPerm('add_issue') - update_perms = HasProjectPerm('modify_issue') - partial_update_perms = HasProjectPerm('modify_issue') + update_perms = CommentAndOrUpdatePerm('modify_issue', 'comment_issue') + partial_update_perms = CommentAndOrUpdatePerm('modify_issue', 'comment_issue') destroy_perms = HasProjectPerm('delete_issue') list_perms = AllowAny() filters_data_perms = AllowAny() diff --git a/taiga/projects/migrations/0041_auto_20160519_1058.py b/taiga/projects/migrations/0041_auto_20160519_1058.py new file mode 100644 index 00000000..c4b0a2fd --- /dev/null +++ b/taiga/projects/migrations/0041_auto_20160519_1058.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.2 on 2016-05-19 10:58 +from __future__ import unicode_literals + +from django.db import migrations +import djorm_pgarray.fields + + +class Migration(migrations.Migration): + + dependencies = [ + ('projects', '0040_remove_memberships_of_cancelled_users_acounts'), + ] + + operations = [ + migrations.AlterField( + model_name='project', + name='public_permissions', + field=djorm_pgarray.fields.TextArrayField(choices=[('view_project', 'View project'), ('view_milestones', 'View milestones'), ('add_milestone', 'Add milestone'), ('modify_milestone', 'Modify milestone'), ('delete_milestone', 'Delete milestone'), ('view_us', 'View user story'), ('add_us', 'Add user story'), ('modify_us', 'Modify user story'), ('comment_us', 'Comment user story'), ('delete_us', 'Delete user story'), ('view_tasks', 'View tasks'), ('add_task', 'Add task'), ('modify_task', 'Modify task'), ('comment_task', 'Comment task'), ('delete_task', 'Delete task'), ('view_issues', 'View issues'), ('add_issue', 'Add issue'), ('modify_issue', 'Modify issue'), ('comment_issue', 'Comment issue'), ('delete_issue', 'Delete issue'), ('view_wiki_pages', 'View wiki pages'), ('add_wiki_page', 'Add wiki page'), ('modify_wiki_page', 'Modify wiki page'), ('comment_wiki_page', 'Comment wiki page'), ('delete_wiki_page', 'Delete wiki page'), ('view_wiki_links', 'View wiki links'), ('add_wiki_link', 'Add wiki link'), ('modify_wiki_link', 'Modify wiki link'), ('delete_wiki_link', 'Delete wiki link')], dbtype='text', default=[], verbose_name='user permissions'), + ), + ] diff --git a/taiga/projects/migrations/0042_auto_20160525_0911.py b/taiga/projects/migrations/0042_auto_20160525_0911.py new file mode 100644 index 00000000..0652df06 --- /dev/null +++ b/taiga/projects/migrations/0042_auto_20160525_0911.py @@ -0,0 +1,67 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.2 on 2016-05-25 09:11 +from __future__ import unicode_literals + +from django.db import migrations + + +UPDATE_PROJECTS_ANON_PERMISSIONS_SQL = """ + UPDATE projects_project + SET + ANON_PERMISSIONS = array_append(ANON_PERMISSIONS, '{comment_permission}') + WHERE + '{base_permission}' = ANY(ANON_PERMISSIONS) + AND + NOT '{comment_permission}' = ANY(ANON_PERMISSIONS) +""" + +UPDATE_PROJECTS_PUBLIC_PERMISSIONS_SQL = """ + UPDATE projects_project + SET + PUBLIC_PERMISSIONS = array_append(PUBLIC_PERMISSIONS, '{comment_permission}') + WHERE + '{base_permission}' = ANY(PUBLIC_PERMISSIONS) + AND + NOT '{comment_permission}' = ANY(PUBLIC_PERMISSIONS) +""" + +class Migration(migrations.Migration): + + dependencies = [ + ('projects', '0041_auto_20160519_1058'), + ] + + operations = [ + # user stories + migrations.RunSQL(UPDATE_PROJECTS_ANON_PERMISSIONS_SQL.format( + base_permission="modify_us", + comment_permission="comment_us") + ), + + migrations.RunSQL(UPDATE_PROJECTS_PUBLIC_PERMISSIONS_SQL.format( + base_permission="modify_us", + comment_permission="comment_us") + ), + + # tasks + migrations.RunSQL(UPDATE_PROJECTS_ANON_PERMISSIONS_SQL.format( + base_permission="modify_task", + comment_permission="comment_task") + ), + + migrations.RunSQL(UPDATE_PROJECTS_PUBLIC_PERMISSIONS_SQL.format( + base_permission="modify_task", + comment_permission="comment_task") + ), + + # issues + migrations.RunSQL(UPDATE_PROJECTS_ANON_PERMISSIONS_SQL.format( + base_permission="modify_issue", + comment_permission="comment_issue") + ), + + migrations.RunSQL(UPDATE_PROJECTS_PUBLIC_PERMISSIONS_SQL.format( + base_permission="modify_issue", + comment_permission="comment_issue") + ) + ] diff --git a/taiga/projects/tasks/permissions.py b/taiga/projects/tasks/permissions.py index 566fc79c..8dfafc41 100644 --- a/taiga/projects/tasks/permissions.py +++ b/taiga/projects/tasks/permissions.py @@ -18,14 +18,16 @@ from taiga.base.api.permissions import TaigaResourcePermission, AllowAny, IsAuthenticated, IsSuperUser from taiga.permissions.permissions import HasProjectPerm, IsProjectAdmin +from taiga.permissions.permissions import CommentAndOrUpdatePerm + class TaskPermission(TaigaResourcePermission): enought_perms = IsProjectAdmin() | IsSuperUser() global_perms = None retrieve_perms = HasProjectPerm('view_tasks') create_perms = HasProjectPerm('add_task') - update_perms = HasProjectPerm('modify_task') - partial_update_perms = HasProjectPerm('modify_task') + update_perms = CommentAndOrUpdatePerm('modify_task', 'comment_task') + partial_update_perms = CommentAndOrUpdatePerm('modify_task', 'comment_task') destroy_perms = HasProjectPerm('delete_task') list_perms = AllowAny() csv_perms = AllowAny() diff --git a/taiga/projects/userstories/permissions.py b/taiga/projects/userstories/permissions.py index 11aa5b73..c91ef2a7 100644 --- a/taiga/projects/userstories/permissions.py +++ b/taiga/projects/userstories/permissions.py @@ -18,14 +18,16 @@ from taiga.base.api.permissions import TaigaResourcePermission, AllowAny, IsAuthenticated, IsSuperUser from taiga.permissions.permissions import HasProjectPerm, IsProjectAdmin +from taiga.permissions.permissions import CommentAndOrUpdatePerm + class UserStoryPermission(TaigaResourcePermission): enought_perms = IsProjectAdmin() | IsSuperUser() global_perms = None retrieve_perms = HasProjectPerm('view_us') create_perms = HasProjectPerm('add_us_to_project') | HasProjectPerm('add_us') - update_perms = HasProjectPerm('modify_us') - partial_update_perms = HasProjectPerm('modify_us') + update_perms = CommentAndOrUpdatePerm('modify_us', 'comment_us') + partial_update_perms = CommentAndOrUpdatePerm('modify_us', 'comment_us') destroy_perms = HasProjectPerm('delete_us') list_perms = AllowAny() filters_data_perms = AllowAny() diff --git a/taiga/projects/wiki/permissions.py b/taiga/projects/wiki/permissions.py index d85d56ea..e60458c7 100644 --- a/taiga/projects/wiki/permissions.py +++ b/taiga/projects/wiki/permissions.py @@ -19,6 +19,8 @@ from taiga.base.api.permissions import (TaigaResourcePermission, HasProjectPerm, IsAuthenticated, IsProjectAdmin, AllowAny, IsSuperUser) +from taiga.permissions.permissions import CommentAndOrUpdatePerm + class WikiPagePermission(TaigaResourcePermission): enought_perms = IsProjectAdmin() | IsSuperUser() @@ -26,8 +28,8 @@ class WikiPagePermission(TaigaResourcePermission): retrieve_perms = HasProjectPerm('view_wiki_pages') by_slug_perms = HasProjectPerm('view_wiki_pages') create_perms = HasProjectPerm('add_wiki_page') - update_perms = HasProjectPerm('modify_wiki_page') - partial_update_perms = HasProjectPerm('modify_wiki_page') + update_perms = CommentAndOrUpdatePerm('modify_wiki_page', 'comment_wiki_page') + partial_update_perms = CommentAndOrUpdatePerm('modify_wiki_page', 'comment_wiki_page') destroy_perms = HasProjectPerm('delete_wiki_page') list_perms = AllowAny() render_perms = AllowAny() diff --git a/taiga/users/migrations/0019_auto_20160519_1058.py b/taiga/users/migrations/0019_auto_20160519_1058.py new file mode 100644 index 00000000..69780084 --- /dev/null +++ b/taiga/users/migrations/0019_auto_20160519_1058.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.2 on 2016-05-19 10:58 +from __future__ import unicode_literals + +from django.db import migrations +import djorm_pgarray.fields + + +class Migration(migrations.Migration): + + dependencies = [ + ('users', '0018_remove_vote_issues_in_roles_permissions_field'), + ] + + operations = [ + migrations.AlterField( + model_name='role', + name='permissions', + field=djorm_pgarray.fields.TextArrayField(choices=[('view_project', 'View project'), ('view_milestones', 'View milestones'), ('add_milestone', 'Add milestone'), ('modify_milestone', 'Modify milestone'), ('delete_milestone', 'Delete milestone'), ('view_us', 'View user story'), ('add_us', 'Add user story'), ('modify_us', 'Modify user story'), ('comment_us', 'Comment user story'), ('delete_us', 'Delete user story'), ('view_tasks', 'View tasks'), ('add_task', 'Add task'), ('modify_task', 'Modify task'), ('comment_task', 'Comment task'), ('delete_task', 'Delete task'), ('view_issues', 'View issues'), ('add_issue', 'Add issue'), ('modify_issue', 'Modify issue'), ('comment_issue', 'Comment issue'), ('delete_issue', 'Delete issue'), ('view_wiki_pages', 'View wiki pages'), ('add_wiki_page', 'Add wiki page'), ('modify_wiki_page', 'Modify wiki page'), ('comment_wiki_page', 'Comment wiki page'), ('delete_wiki_page', 'Delete wiki page'), ('view_wiki_links', 'View wiki links'), ('add_wiki_link', 'Add wiki link'), ('modify_wiki_link', 'Modify wiki link'), ('delete_wiki_link', 'Delete wiki link')], dbtype='text', default=[], verbose_name='permissions'), + ), + ] diff --git a/taiga/users/migrations/0020_auto_20160525_1229.py b/taiga/users/migrations/0020_auto_20160525_1229.py new file mode 100644 index 00000000..765eb3e1 --- /dev/null +++ b/taiga/users/migrations/0020_auto_20160525_1229.py @@ -0,0 +1,48 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.2 on 2016-05-25 12:29 +from __future__ import unicode_literals + +from django.db import migrations + + +UPDATE_ROLES_PERMISSIONS_SQL = """ + UPDATE users_role + SET + PERMISSIONS = array_append(PERMISSIONS, '{comment_permission}') + WHERE + '{base_permission}' = ANY(PERMISSIONS) + AND + NOT '{comment_permission}' = ANY(PERMISSIONS) +""" + +class Migration(migrations.Migration): + + dependencies = [ + ('users', '0019_auto_20160519_1058'), + ] + + operations = [ + # user stories + migrations.RunSQL(UPDATE_ROLES_PERMISSIONS_SQL.format( + base_permission="modify_us", + comment_permission="comment_us") + ), + + # tasks + migrations.RunSQL(UPDATE_ROLES_PERMISSIONS_SQL.format( + base_permission="modify_task", + comment_permission="comment_task") + ), + + # issues + migrations.RunSQL(UPDATE_ROLES_PERMISSIONS_SQL.format( + base_permission="modify_issue", + comment_permission="comment_issue") + ), + + # issues + migrations.RunSQL(UPDATE_ROLES_PERMISSIONS_SQL.format( + base_permission="modify_issue", + comment_permission="comment_issue") + ) + ] diff --git a/tests/integration/resources_permissions/test_issues_resources.py b/tests/integration/resources_permissions/test_issues_resources.py index bb532473..536b4422 100644 --- a/tests/integration/resources_permissions/test_issues_resources.py +++ b/tests/integration/resources_permissions/test_issues_resources.py @@ -132,6 +132,55 @@ def data(): return m +def test_issue_list(client, data): + url = reverse('issues-list') + + response = client.get(url) + issues_data = json.loads(response.content.decode('utf-8')) + assert len(issues_data) == 2 + assert response.status_code == 200 + + client.login(data.registered_user) + + response = client.get(url) + issues_data = json.loads(response.content.decode('utf-8')) + assert len(issues_data) == 2 + assert response.status_code == 200 + + client.login(data.project_member_with_perms) + + response = client.get(url) + issues_data = json.loads(response.content.decode('utf-8')) + assert len(issues_data) == 4 + assert response.status_code == 200 + + client.login(data.project_owner) + + response = client.get(url) + issues_data = json.loads(response.content.decode('utf-8')) + assert len(issues_data) == 4 + assert response.status_code == 200 + + +def test_issue_list_filter_by_project_ok(client, data): + url = "{}?project={}".format(reverse("issues-list"), data.public_project.pk) + + client.login(data.project_owner) + response = client.get(url) + + assert response.status_code == 200 + assert len(response.data) == 1 + + +def test_issue_list_filter_by_project_error(client, data): + url = "{}?project={}".format(reverse("issues-list"), "-ERROR-") + + client.login(data.project_owner) + response = client.get(url) + + assert response.status_code == 400 + + def test_issue_retrieve(client, data): public_url = reverse('issues-detail', kwargs={"pk": data.public_issue.pk}) private_url1 = reverse('issues-detail', kwargs={"pk": data.private_issue1.pk}) @@ -156,7 +205,67 @@ def test_issue_retrieve(client, data): assert results == [401, 403, 403, 200, 200] -def test_issue_update(client, data): +def test_issue_create(client, data): + url = reverse('issues-list') + + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + data.project_owner + ] + + create_data = json.dumps({ + "subject": "test", + "ref": 1, + "project": data.public_project.pk, + "severity": data.public_project.severities.all()[0].pk, + "priority": data.public_project.priorities.all()[0].pk, + "status": data.public_project.issue_statuses.all()[0].pk, + "type": data.public_project.issue_types.all()[0].pk, + }) + results = helper_test_http_method(client, 'post', url, create_data, users) + assert results == [401, 403, 403, 201, 201] + + create_data = json.dumps({ + "subject": "test", + "ref": 2, + "project": data.private_project1.pk, + "severity": data.private_project1.severities.all()[0].pk, + "priority": data.private_project1.priorities.all()[0].pk, + "status": data.private_project1.issue_statuses.all()[0].pk, + "type": data.private_project1.issue_types.all()[0].pk, + }) + results = helper_test_http_method(client, 'post', url, create_data, users) + assert results == [401, 403, 403, 201, 201] + + create_data = json.dumps({ + "subject": "test", + "ref": 3, + "project": data.private_project2.pk, + "severity": data.private_project2.severities.all()[0].pk, + "priority": data.private_project2.priorities.all()[0].pk, + "status": data.private_project2.issue_statuses.all()[0].pk, + "type": data.private_project2.issue_types.all()[0].pk, + }) + results = helper_test_http_method(client, 'post', url, create_data, users) + assert results == [401, 403, 403, 201, 201] + + create_data = json.dumps({ + "subject": "test", + "ref": 3, + "project": data.blocked_project.pk, + "severity": data.blocked_project.severities.all()[0].pk, + "priority": data.blocked_project.priorities.all()[0].pk, + "status": data.blocked_project.issue_statuses.all()[0].pk, + "type": data.blocked_project.issue_types.all()[0].pk, + }) + results = helper_test_http_method(client, 'post', url, create_data, users) + assert results == [401, 403, 403, 451, 451] + + +def test_issue_put_update(client, data): public_url = reverse('issues-detail', kwargs={"pk": data.public_issue.pk}) private_url1 = reverse('issues-detail', kwargs={"pk": data.private_issue1.pk}) private_url2 = reverse('issues-detail', kwargs={"pk": data.private_issue2.pk}) @@ -171,32 +280,116 @@ def test_issue_update(client, data): ] with mock.patch.object(OCCResourceMixin, "_validate_and_update_version"): - issue_data = IssueSerializer(data.public_issue).data - issue_data["subject"] = "test" - issue_data = json.dumps(issue_data) - results = helper_test_http_method(client, 'put', public_url, issue_data, users) - assert results == [401, 403, 403, 200, 200] + issue_data = IssueSerializer(data.public_issue).data + issue_data["subject"] = "test" + issue_data = json.dumps(issue_data) + results = helper_test_http_method(client, 'put', public_url, issue_data, users) + assert results == [401, 403, 403, 200, 200] - issue_data = IssueSerializer(data.private_issue1).data - issue_data["subject"] = "test" - issue_data = json.dumps(issue_data) - results = helper_test_http_method(client, 'put', private_url1, issue_data, users) - assert results == [401, 403, 403, 200, 200] + issue_data = IssueSerializer(data.private_issue1).data + issue_data["subject"] = "test" + issue_data = json.dumps(issue_data) + results = helper_test_http_method(client, 'put', private_url1, issue_data, users) + assert results == [401, 403, 403, 200, 200] - issue_data = IssueSerializer(data.private_issue2).data - issue_data["subject"] = "test" - issue_data = json.dumps(issue_data) - results = helper_test_http_method(client, 'put', private_url2, issue_data, users) - assert results == [401, 403, 403, 200, 200] + issue_data = IssueSerializer(data.private_issue2).data + issue_data["subject"] = "test" + issue_data = json.dumps(issue_data) + results = helper_test_http_method(client, 'put', private_url2, issue_data, users) + assert results == [401, 403, 403, 200, 200] - issue_data = IssueSerializer(data.blocked_issue).data - issue_data["subject"] = "test" - issue_data = json.dumps(issue_data) - results = helper_test_http_method(client, 'put', blocked_url, issue_data, users) - assert results == [401, 403, 403, 451, 451] + issue_data = IssueSerializer(data.blocked_issue).data + issue_data["subject"] = "test" + issue_data = json.dumps(issue_data) + results = helper_test_http_method(client, 'put', blocked_url, issue_data, users) + assert results == [401, 403, 403, 451, 451] -def test_issue_update_with_project_change(client): +def test_issue_put_comment(client, data): + public_url = reverse('issues-detail', kwargs={"pk": data.public_issue.pk}) + private_url1 = reverse('issues-detail', kwargs={"pk": data.private_issue1.pk}) + private_url2 = reverse('issues-detail', kwargs={"pk": data.private_issue2.pk}) + blocked_url = reverse('issues-detail', kwargs={"pk": data.blocked_issue.pk}) + + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + data.project_owner + ] + + with mock.patch.object(OCCResourceMixin, "_validate_and_update_version"): + issue_data = IssueSerializer(data.public_issue).data + issue_data["comment"] = "test comment" + issue_data = json.dumps(issue_data) + results = helper_test_http_method(client, 'put', public_url, issue_data, users) + assert results == [401, 403, 403, 200, 200] + + issue_data = IssueSerializer(data.private_issue1).data + issue_data["comment"] = "test comment" + issue_data = json.dumps(issue_data) + results = helper_test_http_method(client, 'put', private_url1, issue_data, users) + assert results == [401, 403, 403, 200, 200] + + issue_data = IssueSerializer(data.private_issue2).data + issue_data["comment"] = "test comment" + issue_data = json.dumps(issue_data) + results = helper_test_http_method(client, 'put', private_url2, issue_data, users) + assert results == [401, 403, 403, 200, 200] + + issue_data = IssueSerializer(data.blocked_issue).data + issue_data["comment"] = "test comment" + issue_data = json.dumps(issue_data) + results = helper_test_http_method(client, 'put', blocked_url, issue_data, users) + assert results == [401, 403, 403, 451, 451] + + +def test_issue_put_update_and_comment(client, data): + public_url = reverse('issues-detail', kwargs={"pk": data.public_issue.pk}) + private_url1 = reverse('issues-detail', kwargs={"pk": data.private_issue1.pk}) + private_url2 = reverse('issues-detail', kwargs={"pk": data.private_issue2.pk}) + blocked_url = reverse('issues-detail', kwargs={"pk": data.blocked_issue.pk}) + + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + data.project_owner + ] + + with mock.patch.object(OCCResourceMixin, "_validate_and_update_version"): + issue_data = IssueSerializer(data.public_issue).data + issue_data["subject"] = "test" + issue_data["comment"] = "test comment" + issue_data = json.dumps(issue_data) + results = helper_test_http_method(client, 'put', public_url, issue_data, users) + assert results == [401, 403, 403, 200, 200] + + issue_data = IssueSerializer(data.private_issue1).data + issue_data["subject"] = "test" + issue_data["comment"] = "test comment" + issue_data = json.dumps(issue_data) + results = helper_test_http_method(client, 'put', private_url1, issue_data, users) + assert results == [401, 403, 403, 200, 200] + + issue_data = IssueSerializer(data.private_issue2).data + issue_data["subject"] = "test" + issue_data["comment"] = "test comment" + issue_data = json.dumps(issue_data) + results = helper_test_http_method(client, 'put', private_url2, issue_data, users) + assert results == [401, 403, 403, 200, 200] + + issue_data = IssueSerializer(data.blocked_issue).data + issue_data["subject"] = "test" + issue_data["comment"] = "test comment" + issue_data = json.dumps(issue_data) + results = helper_test_http_method(client, 'put', blocked_url, issue_data, users) + assert results == [401, 403, 403, 451, 451] + + +def test_issue_put_update_with_project_change(client): user1 = f.UserFactory.create() user2 = f.UserFactory.create() user3 = f.UserFactory.create() @@ -309,139 +502,7 @@ def test_issue_update_with_project_change(client): issue.save() -def test_issue_delete(client, data): - public_url = reverse('issues-detail', kwargs={"pk": data.public_issue.pk}) - private_url1 = reverse('issues-detail', kwargs={"pk": data.private_issue1.pk}) - private_url2 = reverse('issues-detail', kwargs={"pk": data.private_issue2.pk}) - blocked_url = reverse('issues-detail', kwargs={"pk": data.blocked_issue.pk}) - - users = [ - None, - data.registered_user, - data.project_member_without_perms, - data.project_member_with_perms, - ] - - results = helper_test_http_method(client, 'delete', public_url, None, users) - assert results == [401, 403, 403, 204] - results = helper_test_http_method(client, 'delete', private_url1, None, users) - assert results == [401, 403, 403, 204] - results = helper_test_http_method(client, 'delete', private_url2, None, users) - assert results == [401, 403, 403, 204] - results = helper_test_http_method(client, 'delete', blocked_url, None, users) - assert results == [401, 403, 403, 451] - - -def test_issue_list(client, data): - url = reverse('issues-list') - - response = client.get(url) - issues_data = json.loads(response.content.decode('utf-8')) - assert len(issues_data) == 2 - assert response.status_code == 200 - - client.login(data.registered_user) - - response = client.get(url) - issues_data = json.loads(response.content.decode('utf-8')) - assert len(issues_data) == 2 - assert response.status_code == 200 - - client.login(data.project_member_with_perms) - - response = client.get(url) - issues_data = json.loads(response.content.decode('utf-8')) - assert len(issues_data) == 4 - assert response.status_code == 200 - - client.login(data.project_owner) - - response = client.get(url) - issues_data = json.loads(response.content.decode('utf-8')) - assert len(issues_data) == 4 - assert response.status_code == 200 - - -def test_issue_list_filter_by_project_ok(client, data): - url = "{}?project={}".format(reverse("issues-list"), data.public_project.pk) - - client.login(data.project_owner) - response = client.get(url) - - assert response.status_code == 200 - assert len(response.data) == 1 - - -def test_issue_list_filter_by_project_error(client, data): - url = "{}?project={}".format(reverse("issues-list"), "-ERROR-") - - client.login(data.project_owner) - response = client.get(url) - - assert response.status_code == 400 - - -def test_issue_create(client, data): - url = reverse('issues-list') - - users = [ - None, - data.registered_user, - data.project_member_without_perms, - data.project_member_with_perms, - data.project_owner - ] - - create_data = json.dumps({ - "subject": "test", - "ref": 1, - "project": data.public_project.pk, - "severity": data.public_project.severities.all()[0].pk, - "priority": data.public_project.priorities.all()[0].pk, - "status": data.public_project.issue_statuses.all()[0].pk, - "type": data.public_project.issue_types.all()[0].pk, - }) - results = helper_test_http_method(client, 'post', url, create_data, users) - assert results == [401, 403, 403, 201, 201] - - create_data = json.dumps({ - "subject": "test", - "ref": 2, - "project": data.private_project1.pk, - "severity": data.private_project1.severities.all()[0].pk, - "priority": data.private_project1.priorities.all()[0].pk, - "status": data.private_project1.issue_statuses.all()[0].pk, - "type": data.private_project1.issue_types.all()[0].pk, - }) - results = helper_test_http_method(client, 'post', url, create_data, users) - assert results == [401, 403, 403, 201, 201] - - create_data = json.dumps({ - "subject": "test", - "ref": 3, - "project": data.private_project2.pk, - "severity": data.private_project2.severities.all()[0].pk, - "priority": data.private_project2.priorities.all()[0].pk, - "status": data.private_project2.issue_statuses.all()[0].pk, - "type": data.private_project2.issue_types.all()[0].pk, - }) - results = helper_test_http_method(client, 'post', url, create_data, users) - assert results == [401, 403, 403, 201, 201] - - create_data = json.dumps({ - "subject": "test", - "ref": 3, - "project": data.blocked_project.pk, - "severity": data.blocked_project.severities.all()[0].pk, - "priority": data.blocked_project.priorities.all()[0].pk, - "status": data.blocked_project.issue_statuses.all()[0].pk, - "type": data.blocked_project.issue_types.all()[0].pk, - }) - results = helper_test_http_method(client, 'post', url, create_data, users) - assert results == [401, 403, 403, 451, 451] - - -def test_issue_patch(client, data): +def test_issue_patch_update(client, data): public_url = reverse('issues-detail', kwargs={"pk": data.public_issue.pk}) private_url1 = reverse('issues-detail', kwargs={"pk": data.private_issue1.pk}) private_url2 = reverse('issues-detail', kwargs={"pk": data.private_issue2.pk}) @@ -473,7 +534,110 @@ def test_issue_patch(client, data): assert results == [401, 403, 403, 451, 451] -def test_issue_bulk_create(client, data): +def test_issue_patch_comment(client, data): + public_url = reverse('issues-detail', kwargs={"pk": data.public_issue.pk}) + private_url1 = reverse('issues-detail', kwargs={"pk": data.private_issue1.pk}) + private_url2 = reverse('issues-detail', kwargs={"pk": data.private_issue2.pk}) + blocked_url = reverse('issues-detail', kwargs={"pk": data.blocked_issue.pk}) + + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + data.project_owner + ] + + with mock.patch.object(OCCResourceMixin, "_validate_and_update_version"): + patch_data = json.dumps({"comment": "test comment", "version": data.public_issue.version}) + results = helper_test_http_method(client, 'patch', public_url, patch_data, users) + assert results == [401, 403, 403, 200, 200] + + patch_data = json.dumps({"comment": "test comment", "version": data.private_issue1.version}) + results = helper_test_http_method(client, 'patch', private_url1, patch_data, users) + assert results == [401, 403, 403, 200, 200] + + patch_data = json.dumps({"comment": "test comment", "version": data.private_issue2.version}) + results = helper_test_http_method(client, 'patch', private_url2, patch_data, users) + assert results == [401, 403, 403, 200, 200] + + patch_data = json.dumps({"comment": "test comment", "version": data.blocked_issue.version}) + results = helper_test_http_method(client, 'patch', blocked_url, patch_data, users) + assert results == [401, 403, 403, 451, 451] + + +def test_issue_patch_update_and_comment(client, data): + public_url = reverse('issues-detail', kwargs={"pk": data.public_issue.pk}) + private_url1 = reverse('issues-detail', kwargs={"pk": data.private_issue1.pk}) + private_url2 = reverse('issues-detail', kwargs={"pk": data.private_issue2.pk}) + blocked_url = reverse('issues-detail', kwargs={"pk": data.blocked_issue.pk}) + + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + data.project_owner + ] + + with mock.patch.object(OCCResourceMixin, "_validate_and_update_version"): + patch_data = json.dumps({ + "subject": "test", + "comment": "test comment", + "version": data.public_issue.version + }) + results = helper_test_http_method(client, 'patch', public_url, patch_data, users) + assert results == [401, 403, 403, 200, 200] + + patch_data = json.dumps({ + "subject": "test", + "comment": "test comment", + "version": data.private_issue1.version + }) + results = helper_test_http_method(client, 'patch', private_url1, patch_data, users) + assert results == [401, 403, 403, 200, 200] + + patch_data = json.dumps({ + "subject": "test", + "comment": "test comment", + "version": data.private_issue2.version + }) + results = helper_test_http_method(client, 'patch', private_url2, patch_data, users) + assert results == [401, 403, 403, 200, 200] + + patch_data = json.dumps({ + "subject": "test", + "comment": "test comment", + "version": data.blocked_issue.version + }) + results = helper_test_http_method(client, 'patch', blocked_url, patch_data, users) + assert results == [401, 403, 403, 451, 451] + + +def test_issue_delete(client, data): + public_url = reverse('issues-detail', kwargs={"pk": data.public_issue.pk}) + private_url1 = reverse('issues-detail', kwargs={"pk": data.private_issue1.pk}) + private_url2 = reverse('issues-detail', kwargs={"pk": data.private_issue2.pk}) + blocked_url = reverse('issues-detail', kwargs={"pk": data.blocked_issue.pk}) + + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + ] + + results = helper_test_http_method(client, 'delete', public_url, None, users) + assert results == [401, 403, 403, 204] + results = helper_test_http_method(client, 'delete', private_url1, None, users) + assert results == [401, 403, 403, 204] + results = helper_test_http_method(client, 'delete', private_url2, None, users) + assert results == [401, 403, 403, 204] + results = helper_test_http_method(client, 'delete', blocked_url, None, users) + assert results == [401, 403, 403, 451] + + +def test_issue_action_bulk_create(client, data): data.public_issue.project.default_issue_status = f.IssueStatusFactory() data.public_issue.project.default_issue_type = f.IssueTypeFactory() data.public_issue.project.default_priority = f.PriorityFactory() @@ -633,34 +797,6 @@ def test_issue_voters_retrieve(client, data): assert results == [401, 403, 403, 200, 200] -def test_issues_csv(client, data): - url = reverse('issues-csv') - csv_public_uuid = data.public_project.issues_csv_uuid - csv_private1_uuid = data.private_project1.issues_csv_uuid - csv_private2_uuid = data.private_project2.issues_csv_uuid - csv_blocked_uuid = data.blocked_project.issues_csv_uuid - - users = [ - None, - data.registered_user, - data.project_member_without_perms, - data.project_member_with_perms, - data.project_owner - ] - - results = helper_test_http_method(client, 'get', "{}?uuid={}".format(url, csv_public_uuid), None, users) - assert results == [200, 200, 200, 200, 200] - - results = helper_test_http_method(client, 'get', "{}?uuid={}".format(url, csv_private1_uuid), None, users) - assert results == [200, 200, 200, 200, 200] - - results = helper_test_http_method(client, 'get', "{}?uuid={}".format(url, csv_private2_uuid), None, users) - assert results == [200, 200, 200, 200, 200] - - results = helper_test_http_method(client, 'get', "{}?uuid={}".format(url, csv_blocked_uuid), None, users) - assert results == [200, 200, 200, 200, 200] - - def test_issue_action_watch(client, data): public_url = reverse('issues-watch', kwargs={"pk": data.public_issue.pk}) private_url1 = reverse('issues-watch', kwargs={"pk": data.private_issue1.pk}) @@ -762,3 +898,31 @@ def test_issue_watchers_retrieve(client, data): assert results == [401, 403, 403, 200, 200] results = helper_test_http_method(client, 'get', blocked_url, None, users) assert results == [401, 403, 403, 200, 200] + + +def test_issues_csv(client, data): + url = reverse('issues-csv') + csv_public_uuid = data.public_project.issues_csv_uuid + csv_private1_uuid = data.private_project1.issues_csv_uuid + csv_private2_uuid = data.private_project2.issues_csv_uuid + csv_blocked_uuid = data.blocked_project.issues_csv_uuid + + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + data.project_owner + ] + + results = helper_test_http_method(client, 'get', "{}?uuid={}".format(url, csv_public_uuid), None, users) + assert results == [200, 200, 200, 200, 200] + + results = helper_test_http_method(client, 'get', "{}?uuid={}".format(url, csv_private1_uuid), None, users) + assert results == [200, 200, 200, 200, 200] + + results = helper_test_http_method(client, 'get', "{}?uuid={}".format(url, csv_private2_uuid), None, users) + assert results == [200, 200, 200, 200, 200] + + results = helper_test_http_method(client, 'get', "{}?uuid={}".format(url, csv_blocked_uuid), None, users) + assert results == [200, 200, 200, 200, 200] diff --git a/tests/integration/resources_permissions/test_tasks_resources.py b/tests/integration/resources_permissions/test_tasks_resources.py index aff81389..63a1d63e 100644 --- a/tests/integration/resources_permissions/test_tasks_resources.py +++ b/tests/integration/resources_permissions/test_tasks_resources.py @@ -142,6 +142,36 @@ def data(): return m +def test_task_list(client, data): + url = reverse('tasks-list') + + response = client.get(url) + tasks_data = json.loads(response.content.decode('utf-8')) + assert len(tasks_data) == 2 + assert response.status_code == 200 + + client.login(data.registered_user) + + response = client.get(url) + tasks_data = json.loads(response.content.decode('utf-8')) + assert len(tasks_data) == 2 + assert response.status_code == 200 + + client.login(data.project_member_with_perms) + + response = client.get(url) + tasks_data = json.loads(response.content.decode('utf-8')) + assert len(tasks_data) == 4 + assert response.status_code == 200 + + client.login(data.project_owner) + + response = client.get(url) + tasks_data = json.loads(response.content.decode('utf-8')) + assert len(tasks_data) == 4 + assert response.status_code == 200 + + def test_task_retrieve(client, data): public_url = reverse('tasks-detail', kwargs={"pk": data.public_task.pk}) private_url1 = reverse('tasks-detail', kwargs={"pk": data.private_task1.pk}) @@ -166,7 +196,55 @@ def test_task_retrieve(client, data): assert results == [401, 403, 403, 200, 200] -def test_task_update(client, data): +def test_task_create(client, data): + url = reverse('tasks-list') + + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + data.project_owner + ] + + create_data = json.dumps({ + "subject": "test", + "ref": 1, + "project": data.public_project.pk, + "status": data.public_project.task_statuses.all()[0].pk, + }) + results = helper_test_http_method(client, 'post', url, create_data, users) + assert results == [401, 403, 403, 201, 201] + + create_data = json.dumps({ + "subject": "test", + "ref": 2, + "project": data.private_project1.pk, + "status": data.private_project1.task_statuses.all()[0].pk, + }) + results = helper_test_http_method(client, 'post', url, create_data, users) + assert results == [401, 403, 403, 201, 201] + + create_data = json.dumps({ + "subject": "test", + "ref": 3, + "project": data.private_project2.pk, + "status": data.private_project2.task_statuses.all()[0].pk, + }) + results = helper_test_http_method(client, 'post', url, create_data, users) + assert results == [401, 403, 403, 201, 201] + + create_data = json.dumps({ + "subject": "test", + "ref": 3, + "project": data.blocked_project.pk, + "status": data.blocked_project.task_statuses.all()[0].pk, + }) + results = helper_test_http_method(client, 'post', url, create_data, users) + assert results == [401, 403, 403, 451, 451] + + +def test_task_put_update(client, data): public_url = reverse('tasks-detail', kwargs={"pk": data.public_task.pk}) private_url1 = reverse('tasks-detail', kwargs={"pk": data.private_task1.pk}) private_url2 = reverse('tasks-detail', kwargs={"pk": data.private_task2.pk}) @@ -181,32 +259,116 @@ def test_task_update(client, data): ] with mock.patch.object(OCCResourceMixin, "_validate_and_update_version"): - task_data = TaskSerializer(data.public_task).data - task_data["subject"] = "test" - task_data = json.dumps(task_data) - results = helper_test_http_method(client, 'put', public_url, task_data, users) - assert results == [401, 403, 403, 200, 200] + task_data = TaskSerializer(data.public_task).data + task_data["subject"] = "test" + task_data = json.dumps(task_data) + results = helper_test_http_method(client, 'put', public_url, task_data, users) + assert results == [401, 403, 403, 200, 200] - task_data = TaskSerializer(data.private_task1).data - task_data["subject"] = "test" - task_data = json.dumps(task_data) - results = helper_test_http_method(client, 'put', private_url1, task_data, users) - assert results == [401, 403, 403, 200, 200] + task_data = TaskSerializer(data.private_task1).data + task_data["subject"] = "test" + task_data = json.dumps(task_data) + results = helper_test_http_method(client, 'put', private_url1, task_data, users) + assert results == [401, 403, 403, 200, 200] - task_data = TaskSerializer(data.private_task2).data - task_data["subject"] = "test" - task_data = json.dumps(task_data) - results = helper_test_http_method(client, 'put', private_url2, task_data, users) - assert results == [401, 403, 403, 200, 200] + task_data = TaskSerializer(data.private_task2).data + task_data["subject"] = "test" + task_data = json.dumps(task_data) + results = helper_test_http_method(client, 'put', private_url2, task_data, users) + assert results == [401, 403, 403, 200, 200] - task_data = TaskSerializer(data.blocked_task).data - task_data["subject"] = "test" - task_data = json.dumps(task_data) - results = helper_test_http_method(client, 'put', blocked_url, task_data, users) - assert results == [401, 403, 403, 451, 451] + task_data = TaskSerializer(data.blocked_task).data + task_data["subject"] = "test" + task_data = json.dumps(task_data) + results = helper_test_http_method(client, 'put', blocked_url, task_data, users) + assert results == [401, 403, 403, 451, 451] -def test_task_update_with_project_change(client): +def test_task_put_comment(client, data): + public_url = reverse('tasks-detail', kwargs={"pk": data.public_task.pk}) + private_url1 = reverse('tasks-detail', kwargs={"pk": data.private_task1.pk}) + private_url2 = reverse('tasks-detail', kwargs={"pk": data.private_task2.pk}) + blocked_url = reverse('tasks-detail', kwargs={"pk": data.blocked_task.pk}) + + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + data.project_owner + ] + + with mock.patch.object(OCCResourceMixin, "_validate_and_update_version"): + task_data = TaskSerializer(data.public_task).data + task_data["comment"] = "test comment" + task_data = json.dumps(task_data) + results = helper_test_http_method(client, 'put', public_url, task_data, users) + assert results == [401, 403, 403, 200, 200] + + task_data = TaskSerializer(data.private_task1).data + task_data["comment"] = "test comment" + task_data = json.dumps(task_data) + results = helper_test_http_method(client, 'put', private_url1, task_data, users) + assert results == [401, 403, 403, 200, 200] + + task_data = TaskSerializer(data.private_task2).data + task_data["comment"] = "test comment" + task_data = json.dumps(task_data) + results = helper_test_http_method(client, 'put', private_url2, task_data, users) + assert results == [401, 403, 403, 200, 200] + + task_data = TaskSerializer(data.blocked_task).data + task_data["comment"] = "test comment" + task_data = json.dumps(task_data) + results = helper_test_http_method(client, 'put', blocked_url, task_data, users) + assert results == [401, 403, 403, 451, 451] + + +def test_task_put_update_and_comment(client, data): + public_url = reverse('tasks-detail', kwargs={"pk": data.public_task.pk}) + private_url1 = reverse('tasks-detail', kwargs={"pk": data.private_task1.pk}) + private_url2 = reverse('tasks-detail', kwargs={"pk": data.private_task2.pk}) + blocked_url = reverse('tasks-detail', kwargs={"pk": data.blocked_task.pk}) + + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + data.project_owner + ] + + with mock.patch.object(OCCResourceMixin, "_validate_and_update_version"): + task_data = TaskSerializer(data.public_task).data + task_data["subject"] = "test" + task_data["comment"] = "test comment" + task_data = json.dumps(task_data) + results = helper_test_http_method(client, 'put', public_url, task_data, users) + assert results == [401, 403, 403, 200, 200] + + task_data = TaskSerializer(data.private_task1).data + task_data["subject"] = "test" + task_data["comment"] = "test comment" + task_data = json.dumps(task_data) + results = helper_test_http_method(client, 'put', private_url1, task_data, users) + assert results == [401, 403, 403, 200, 200] + + task_data = TaskSerializer(data.private_task2).data + task_data["subject"] = "test" + task_data["comment"] = "test comment" + task_data = json.dumps(task_data) + results = helper_test_http_method(client, 'put', private_url2, task_data, users) + assert results == [401, 403, 403, 200, 200] + + task_data = TaskSerializer(data.blocked_task).data + task_data["subject"] = "test" + task_data["comment"] = "test comment" + task_data = json.dumps(task_data) + results = helper_test_http_method(client, 'put', blocked_url, task_data, users) + assert results == [401, 403, 403, 451, 451] + + +def test_task_put_update_with_project_change(client): user1 = f.UserFactory.create() user2 = f.UserFactory.create() user3 = f.UserFactory.create() @@ -301,107 +463,7 @@ def test_task_update_with_project_change(client): task.save() -def test_task_delete(client, data): - public_url = reverse('tasks-detail', kwargs={"pk": data.public_task.pk}) - private_url1 = reverse('tasks-detail', kwargs={"pk": data.private_task1.pk}) - private_url2 = reverse('tasks-detail', kwargs={"pk": data.private_task2.pk}) - blocked_url = reverse('tasks-detail', kwargs={"pk": data.blocked_task.pk}) - - users = [ - None, - data.registered_user, - data.project_member_without_perms, - data.project_member_with_perms, - ] - results = helper_test_http_method(client, 'delete', public_url, None, users) - assert results == [401, 403, 403, 204] - results = helper_test_http_method(client, 'delete', private_url1, None, users) - assert results == [401, 403, 403, 204] - results = helper_test_http_method(client, 'delete', private_url2, None, users) - assert results == [401, 403, 403, 204] - results = helper_test_http_method(client, 'delete', blocked_url, None, users) - assert results == [401, 403, 403, 451] - - -def test_task_list(client, data): - url = reverse('tasks-list') - - response = client.get(url) - tasks_data = json.loads(response.content.decode('utf-8')) - assert len(tasks_data) == 2 - assert response.status_code == 200 - - client.login(data.registered_user) - - response = client.get(url) - tasks_data = json.loads(response.content.decode('utf-8')) - assert len(tasks_data) == 2 - assert response.status_code == 200 - - client.login(data.project_member_with_perms) - - response = client.get(url) - tasks_data = json.loads(response.content.decode('utf-8')) - assert len(tasks_data) == 4 - assert response.status_code == 200 - - client.login(data.project_owner) - - response = client.get(url) - tasks_data = json.loads(response.content.decode('utf-8')) - assert len(tasks_data) == 4 - assert response.status_code == 200 - - -def test_task_create(client, data): - url = reverse('tasks-list') - - users = [ - None, - data.registered_user, - data.project_member_without_perms, - data.project_member_with_perms, - data.project_owner - ] - - create_data = json.dumps({ - "subject": "test", - "ref": 1, - "project": data.public_project.pk, - "status": data.public_project.task_statuses.all()[0].pk, - }) - results = helper_test_http_method(client, 'post', url, create_data, users) - assert results == [401, 403, 403, 201, 201] - - create_data = json.dumps({ - "subject": "test", - "ref": 2, - "project": data.private_project1.pk, - "status": data.private_project1.task_statuses.all()[0].pk, - }) - results = helper_test_http_method(client, 'post', url, create_data, users) - assert results == [401, 403, 403, 201, 201] - - create_data = json.dumps({ - "subject": "test", - "ref": 3, - "project": data.private_project2.pk, - "status": data.private_project2.task_statuses.all()[0].pk, - }) - results = helper_test_http_method(client, 'post', url, create_data, users) - assert results == [401, 403, 403, 201, 201] - - create_data = json.dumps({ - "subject": "test", - "ref": 3, - "project": data.blocked_project.pk, - "status": data.blocked_project.task_statuses.all()[0].pk, - }) - results = helper_test_http_method(client, 'post', url, create_data, users) - assert results == [401, 403, 403, 451, 451] - - -def test_task_patch(client, data): +def test_task_patch_update(client, data): public_url = reverse('tasks-detail', kwargs={"pk": data.public_task.pk}) private_url1 = reverse('tasks-detail', kwargs={"pk": data.private_task1.pk}) private_url2 = reverse('tasks-detail', kwargs={"pk": data.private_task2.pk}) @@ -433,6 +495,108 @@ def test_task_patch(client, data): assert results == [401, 403, 403, 451, 451] +def test_task_patch_comment(client, data): + public_url = reverse('tasks-detail', kwargs={"pk": data.public_task.pk}) + private_url1 = reverse('tasks-detail', kwargs={"pk": data.private_task1.pk}) + private_url2 = reverse('tasks-detail', kwargs={"pk": data.private_task2.pk}) + blocked_url = reverse('tasks-detail', kwargs={"pk": data.blocked_task.pk}) + + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + data.project_owner + ] + + with mock.patch.object(OCCResourceMixin, "_validate_and_update_version"): + patch_data = json.dumps({"comment": "test comment", "version": data.public_task.version}) + results = helper_test_http_method(client, 'patch', public_url, patch_data, users) + assert results == [401, 403, 403, 200, 200] + + patch_data = json.dumps({"comment": "test comment", "version": data.private_task1.version}) + results = helper_test_http_method(client, 'patch', private_url1, patch_data, users) + assert results == [401, 403, 403, 200, 200] + + patch_data = json.dumps({"comment": "test comment", "version": data.private_task2.version}) + results = helper_test_http_method(client, 'patch', private_url2, patch_data, users) + assert results == [401, 403, 403, 200, 200] + + patch_data = json.dumps({"comment": "test comment", "version": data.blocked_task.version}) + results = helper_test_http_method(client, 'patch', blocked_url, patch_data, users) + assert results == [401, 403, 403, 451, 451] + + +def test_task_patch_update_and_comment(client, data): + public_url = reverse('tasks-detail', kwargs={"pk": data.public_task.pk}) + private_url1 = reverse('tasks-detail', kwargs={"pk": data.private_task1.pk}) + private_url2 = reverse('tasks-detail', kwargs={"pk": data.private_task2.pk}) + blocked_url = reverse('tasks-detail', kwargs={"pk": data.blocked_task.pk}) + + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + data.project_owner + ] + + with mock.patch.object(OCCResourceMixin, "_validate_and_update_version"): + patch_data = json.dumps({ + "subject": "test", + "comment": "test comment", + "version": data.public_task.version + }) + results = helper_test_http_method(client, 'patch', public_url, patch_data, users) + assert results == [401, 403, 403, 200, 200] + + patch_data = json.dumps({ + "subject": "test", + "comment": "test comment", + "version": data.private_task1.version + }) + results = helper_test_http_method(client, 'patch', private_url1, patch_data, users) + assert results == [401, 403, 403, 200, 200] + + patch_data = json.dumps({ + "subject": "test", + "comment": "test comment", + "version": data.private_task2.version + }) + results = helper_test_http_method(client, 'patch', private_url2, patch_data, users) + assert results == [401, 403, 403, 200, 200] + + patch_data = json.dumps({ + "subject": "test", + "comment": "test comment", + "version": data.blocked_task.version + }) + results = helper_test_http_method(client, 'patch', blocked_url, patch_data, users) + assert results == [401, 403, 403, 451, 451] + + +def test_task_delete(client, data): + public_url = reverse('tasks-detail', kwargs={"pk": data.public_task.pk}) + private_url1 = reverse('tasks-detail', kwargs={"pk": data.private_task1.pk}) + private_url2 = reverse('tasks-detail', kwargs={"pk": data.private_task2.pk}) + blocked_url = reverse('tasks-detail', kwargs={"pk": data.blocked_task.pk}) + + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + ] + results = helper_test_http_method(client, 'delete', public_url, None, users) + assert results == [401, 403, 403, 204] + results = helper_test_http_method(client, 'delete', private_url1, None, users) + assert results == [401, 403, 403, 204] + results = helper_test_http_method(client, 'delete', private_url2, None, users) + assert results == [401, 403, 403, 204] + results = helper_test_http_method(client, 'delete', blocked_url, None, users) + assert results == [401, 403, 403, 451] + + def test_task_action_bulk_create(client, data): url = reverse('tasks-bulk-create') @@ -586,34 +750,6 @@ def test_task_voters_retrieve(client, data): assert results == [401, 403, 403, 200, 200] -def test_tasks_csv(client, data): - url = reverse('tasks-csv') - csv_public_uuid = data.public_project.tasks_csv_uuid - csv_private1_uuid = data.private_project1.tasks_csv_uuid - csv_private2_uuid = data.private_project1.tasks_csv_uuid - csv_blocked_uuid = data.blocked_project.tasks_csv_uuid - - users = [ - None, - data.registered_user, - data.project_member_without_perms, - data.project_member_with_perms, - data.project_owner - ] - - results = helper_test_http_method(client, 'get', "{}?uuid={}".format(url, csv_public_uuid), None, users) - assert results == [200, 200, 200, 200, 200] - - results = helper_test_http_method(client, 'get', "{}?uuid={}".format(url, csv_private1_uuid), None, users) - assert results == [200, 200, 200, 200, 200] - - results = helper_test_http_method(client, 'get', "{}?uuid={}".format(url, csv_private2_uuid), None, users) - assert results == [200, 200, 200, 200, 200] - - results = helper_test_http_method(client, 'get', "{}?uuid={}".format(url, csv_blocked_uuid), None, users) - assert results == [200, 200, 200, 200, 200] - - def test_task_action_watch(client, data): public_url = reverse('tasks-watch', kwargs={"pk": data.public_task.pk}) private_url1 = reverse('tasks-watch', kwargs={"pk": data.private_task1.pk}) @@ -716,3 +852,31 @@ def test_task_watchers_retrieve(client, data): assert results == [401, 403, 403, 200, 200] results = helper_test_http_method(client, 'get', blocked_url, None, users) assert results == [401, 403, 403, 200, 200] + + +def test_tasks_csv(client, data): + url = reverse('tasks-csv') + csv_public_uuid = data.public_project.tasks_csv_uuid + csv_private1_uuid = data.private_project1.tasks_csv_uuid + csv_private2_uuid = data.private_project1.tasks_csv_uuid + csv_blocked_uuid = data.blocked_project.tasks_csv_uuid + + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + data.project_owner + ] + + results = helper_test_http_method(client, 'get', "{}?uuid={}".format(url, csv_public_uuid), None, users) + assert results == [200, 200, 200, 200, 200] + + results = helper_test_http_method(client, 'get', "{}?uuid={}".format(url, csv_private1_uuid), None, users) + assert results == [200, 200, 200, 200, 200] + + results = helper_test_http_method(client, 'get', "{}?uuid={}".format(url, csv_private2_uuid), None, users) + assert results == [200, 200, 200, 200, 200] + + results = helper_test_http_method(client, 'get', "{}?uuid={}".format(url, csv_blocked_uuid), None, users) + assert results == [200, 200, 200, 200, 200] diff --git a/tests/integration/resources_permissions/test_userstories_resources.py b/tests/integration/resources_permissions/test_userstories_resources.py index 4da7b081..cb5f78ff 100644 --- a/tests/integration/resources_permissions/test_userstories_resources.py +++ b/tests/integration/resources_permissions/test_userstories_resources.py @@ -138,6 +138,36 @@ def data(): return m +def test_user_story_list(client, data): + url = reverse('userstories-list') + + response = client.get(url) + userstories_data = json.loads(response.content.decode('utf-8')) + assert len(userstories_data) == 2 + assert response.status_code == 200 + + client.login(data.registered_user) + + response = client.get(url) + userstories_data = json.loads(response.content.decode('utf-8')) + assert len(userstories_data) == 2 + assert response.status_code == 200 + + client.login(data.project_member_with_perms) + + response = client.get(url) + userstories_data = json.loads(response.content.decode('utf-8')) + assert len(userstories_data) == 4 + assert response.status_code == 200 + + client.login(data.project_owner) + + response = client.get(url) + userstories_data = json.loads(response.content.decode('utf-8')) + assert len(userstories_data) == 4 + assert response.status_code == 200 + + def test_user_story_retrieve(client, data): public_url = reverse('userstories-detail', kwargs={"pk": data.public_user_story.pk}) private_url1 = reverse('userstories-detail', kwargs={"pk": data.private_user_story1.pk}) @@ -162,7 +192,35 @@ def test_user_story_retrieve(client, data): assert results == [401, 403, 403, 200, 200] -def test_user_story_update(client, data): +def test_user_story_create(client, data): + url = reverse('userstories-list') + + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + data.project_owner + ] + + create_data = json.dumps({"subject": "test", "ref": 1, "project": data.public_project.pk}) + results = helper_test_http_method(client, 'post', url, create_data, users) + assert results == [401, 403, 403, 201, 201] + + create_data = json.dumps({"subject": "test", "ref": 2, "project": data.private_project1.pk}) + results = helper_test_http_method(client, 'post', url, create_data, users) + assert results == [401, 403, 403, 201, 201] + + create_data = json.dumps({"subject": "test", "ref": 3, "project": data.private_project2.pk}) + results = helper_test_http_method(client, 'post', url, create_data, users) + assert results == [401, 403, 403, 201, 201] + + create_data = json.dumps({"subject": "test", "ref": 4, "project": data.blocked_project.pk}) + results = helper_test_http_method(client, 'post', url, create_data, users) + assert results == [401, 403, 403, 451, 451] + + +def test_user_story_put_update(client, data): public_url = reverse('userstories-detail', kwargs={"pk": data.public_user_story.pk}) private_url1 = reverse('userstories-detail', kwargs={"pk": data.private_user_story1.pk}) private_url2 = reverse('userstories-detail', kwargs={"pk": data.private_user_story2.pk}) @@ -201,7 +259,92 @@ def test_user_story_update(client, data): results = helper_test_http_method(client, 'put', blocked_url, user_story_data, users) assert results == [401, 403, 403, 451, 451] -def test_user_story_update_with_project_change(client): + +def test_user_story_put_comment(client, data): + public_url = reverse('userstories-detail', kwargs={"pk": data.public_user_story.pk}) + private_url1 = reverse('userstories-detail', kwargs={"pk": data.private_user_story1.pk}) + private_url2 = reverse('userstories-detail', kwargs={"pk": data.private_user_story2.pk}) + blocked_url = reverse('userstories-detail', kwargs={"pk": data.blocked_user_story.pk}) + + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + data.project_owner + ] + + with mock.patch.object(OCCResourceMixin, "_validate_and_update_version"): + user_story_data = UserStorySerializer(data.public_user_story).data + user_story_data["comment"] = "test comment" + user_story_data = json.dumps(user_story_data) + results = helper_test_http_method(client, 'put', public_url, user_story_data, users) + assert results == [401, 403, 403, 200, 200] + + user_story_data = UserStorySerializer(data.private_user_story1).data + user_story_data["comment"] = "test comment" + user_story_data = json.dumps(user_story_data) + results = helper_test_http_method(client, 'put', private_url1, user_story_data, users) + assert results == [401, 403, 403, 200, 200] + + user_story_data = UserStorySerializer(data.private_user_story2).data + user_story_data["comment"] = "test comment" + user_story_data = json.dumps(user_story_data) + results = helper_test_http_method(client, 'put', private_url2, user_story_data, users) + assert results == [401, 403, 403, 200, 200] + + user_story_data = UserStorySerializer(data.blocked_user_story).data + user_story_data["comment"] = "test comment" + user_story_data = json.dumps(user_story_data) + results = helper_test_http_method(client, 'put', blocked_url, user_story_data, users) + assert results == [401, 403, 403, 451, 451] + + +def test_user_story_put_update_and_comment(client, data): + public_url = reverse('userstories-detail', kwargs={"pk": data.public_user_story.pk}) + private_url1 = reverse('userstories-detail', kwargs={"pk": data.private_user_story1.pk}) + private_url2 = reverse('userstories-detail', kwargs={"pk": data.private_user_story2.pk}) + blocked_url = reverse('userstories-detail', kwargs={"pk": data.blocked_user_story.pk}) + + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + data.project_owner + ] + + with mock.patch.object(OCCResourceMixin, "_validate_and_update_version"): + user_story_data = UserStorySerializer(data.public_user_story).data + user_story_data["subject"] = "test" + user_story_data["comment"] = "test comment" + user_story_data = json.dumps(user_story_data) + results = helper_test_http_method(client, 'put', public_url, user_story_data, users) + assert results == [401, 403, 403, 200, 200] + + user_story_data = UserStorySerializer(data.private_user_story1).data + user_story_data["subject"] = "test" + user_story_data["comment"] = "test comment" + user_story_data = json.dumps(user_story_data) + results = helper_test_http_method(client, 'put', private_url1, user_story_data, users) + assert results == [401, 403, 403, 200, 200] + + user_story_data = UserStorySerializer(data.private_user_story2).data + user_story_data["subject"] = "test" + user_story_data["comment"] = "test comment" + user_story_data = json.dumps(user_story_data) + results = helper_test_http_method(client, 'put', private_url2, user_story_data, users) + assert results == [401, 403, 403, 200, 200] + + user_story_data = UserStorySerializer(data.blocked_user_story).data + user_story_data["subject"] = "test" + user_story_data["comment"] = "test comment" + user_story_data = json.dumps(user_story_data) + results = helper_test_http_method(client, 'put', blocked_url, user_story_data, users) + assert results == [401, 403, 403, 451, 451] + + +def test_user_story_put_update_with_project_change(client): user1 = f.UserFactory.create() user2 = f.UserFactory.create() user3 = f.UserFactory.create() @@ -296,87 +439,7 @@ def test_user_story_update_with_project_change(client): us.save() -def test_user_story_delete(client, data): - public_url = reverse('userstories-detail', kwargs={"pk": data.public_user_story.pk}) - private_url1 = reverse('userstories-detail', kwargs={"pk": data.private_user_story1.pk}) - private_url2 = reverse('userstories-detail', kwargs={"pk": data.private_user_story2.pk}) - blocked_url = reverse('userstories-detail', kwargs={"pk": data.blocked_user_story.pk}) - - users = [ - None, - data.registered_user, - data.project_member_without_perms, - data.project_member_with_perms, - ] - results = helper_test_http_method(client, 'delete', public_url, None, users) - assert results == [401, 403, 403, 204] - results = helper_test_http_method(client, 'delete', private_url1, None, users) - assert results == [401, 403, 403, 204] - results = helper_test_http_method(client, 'delete', private_url2, None, users) - assert results == [401, 403, 403, 204] - results = helper_test_http_method(client, 'delete', blocked_url, None, users) - assert results == [401, 403, 403, 451] - - -def test_user_story_list(client, data): - url = reverse('userstories-list') - - response = client.get(url) - userstories_data = json.loads(response.content.decode('utf-8')) - assert len(userstories_data) == 2 - assert response.status_code == 200 - - client.login(data.registered_user) - - response = client.get(url) - userstories_data = json.loads(response.content.decode('utf-8')) - assert len(userstories_data) == 2 - assert response.status_code == 200 - - client.login(data.project_member_with_perms) - - response = client.get(url) - userstories_data = json.loads(response.content.decode('utf-8')) - assert len(userstories_data) == 4 - assert response.status_code == 200 - - client.login(data.project_owner) - - response = client.get(url) - userstories_data = json.loads(response.content.decode('utf-8')) - assert len(userstories_data) == 4 - assert response.status_code == 200 - - -def test_user_story_create(client, data): - url = reverse('userstories-list') - - users = [ - None, - data.registered_user, - data.project_member_without_perms, - data.project_member_with_perms, - data.project_owner - ] - - create_data = json.dumps({"subject": "test", "ref": 1, "project": data.public_project.pk}) - results = helper_test_http_method(client, 'post', url, create_data, users) - assert results == [401, 403, 403, 201, 201] - - create_data = json.dumps({"subject": "test", "ref": 2, "project": data.private_project1.pk}) - results = helper_test_http_method(client, 'post', url, create_data, users) - assert results == [401, 403, 403, 201, 201] - - create_data = json.dumps({"subject": "test", "ref": 3, "project": data.private_project2.pk}) - results = helper_test_http_method(client, 'post', url, create_data, users) - assert results == [401, 403, 403, 201, 201] - - create_data = json.dumps({"subject": "test", "ref": 4, "project": data.blocked_project.pk}) - results = helper_test_http_method(client, 'post', url, create_data, users) - assert results == [401, 403, 403, 451, 451] - - -def test_user_story_patch(client, data): +def test_user_story_patch_update(client, data): public_url = reverse('userstories-detail', kwargs={"pk": data.public_user_story.pk}) private_url1 = reverse('userstories-detail', kwargs={"pk": data.private_user_story1.pk}) private_url2 = reverse('userstories-detail', kwargs={"pk": data.private_user_story2.pk}) @@ -408,6 +471,109 @@ def test_user_story_patch(client, data): assert results == [401, 403, 403, 451, 451] +def test_user_story_patch_comment(client, data): + public_url = reverse('userstories-detail', kwargs={"pk": data.public_user_story.pk}) + private_url1 = reverse('userstories-detail', kwargs={"pk": data.private_user_story1.pk}) + private_url2 = reverse('userstories-detail', kwargs={"pk": data.private_user_story2.pk}) + blocked_url = reverse('userstories-detail', kwargs={"pk": data.blocked_user_story.pk}) + + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + data.project_owner + ] + + with mock.patch.object(OCCResourceMixin, "_validate_and_update_version"): + patch_data = json.dumps({"comment": "test comment", "version": data.public_user_story.version}) + results = helper_test_http_method(client, 'patch', public_url, patch_data, users) + assert results == [401, 403, 403, 200, 200] + + patch_data = json.dumps({"comment": "test comment", "version": data.private_user_story1.version}) + results = helper_test_http_method(client, 'patch', private_url1, patch_data, users) + assert results == [401, 403, 403, 200, 200] + + patch_data = json.dumps({"comment": "test comment", "version": data.private_user_story2.version}) + results = helper_test_http_method(client, 'patch', private_url2, patch_data, users) + assert results == [401, 403, 403, 200, 200] + + patch_data = json.dumps({"comment": "test comment", "version": data.blocked_user_story.version}) + results = helper_test_http_method(client, 'patch', blocked_url, patch_data, users) + assert results == [401, 403, 403, 451, 451] + + +def test_user_story_patch_update_and_comment(client, data): + public_url = reverse('userstories-detail', kwargs={"pk": data.public_user_story.pk}) + private_url1 = reverse('userstories-detail', kwargs={"pk": data.private_user_story1.pk}) + private_url2 = reverse('userstories-detail', kwargs={"pk": data.private_user_story2.pk}) + blocked_url = reverse('userstories-detail', kwargs={"pk": data.blocked_user_story.pk}) + + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + data.project_owner + ] + + with mock.patch.object(OCCResourceMixin, "_validate_and_update_version"): + patch_data = json.dumps({ + "subject": "test", + "comment": "test comment", + "version": data.public_user_story.version + }) + results = helper_test_http_method(client, 'patch', public_url, patch_data, users) + assert results == [401, 403, 403, 200, 200] + + patch_data = json.dumps({ + "subject": "test", + "comment": "test comment", + "version": data.private_user_story1.version + }) + results = helper_test_http_method(client, 'patch', private_url1, patch_data, users) + assert results == [401, 403, 403, 200, 200] + + patch_data = json.dumps({ + "subject": "test", + "comment": "test comment", + "version": data.private_user_story2.version + }) + results = helper_test_http_method(client, 'patch', private_url2, patch_data, users) + assert results == [401, 403, 403, 200, 200] + + patch_data = json.dumps({ + "subject": "test", + "comment": "test comment", + "version": data.blocked_user_story.version + }) + results = helper_test_http_method(client, 'patch', blocked_url, patch_data, users) + assert results == [401, 403, 403, 451, 451] + + +def test_user_story_delete(client, data): + public_url = reverse('userstories-detail', kwargs={"pk": data.public_user_story.pk}) + private_url1 = reverse('userstories-detail', kwargs={"pk": data.private_user_story1.pk}) + private_url2 = reverse('userstories-detail', kwargs={"pk": data.private_user_story2.pk}) + blocked_url = reverse('userstories-detail', kwargs={"pk": data.blocked_user_story.pk}) + + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + ] + results = helper_test_http_method(client, 'delete', public_url, None, users) + assert results == [401, 403, 403, 204] + results = helper_test_http_method(client, 'delete', private_url1, None, users) + assert results == [401, 403, 403, 204] + results = helper_test_http_method(client, 'delete', private_url2, None, users) + assert results == [401, 403, 403, 204] + results = helper_test_http_method(client, 'delete', blocked_url, None, users) + assert results == [401, 403, 403, 451] + + + def test_user_story_action_bulk_create(client, data): url = reverse('userstories-bulk-create') @@ -580,30 +746,6 @@ def test_user_story_voters_retrieve(client, data): assert results == [401, 403, 403, 200, 200] -def test_user_stories_csv(client, data): - url = reverse('userstories-csv') - csv_public_uuid = data.public_project.userstories_csv_uuid - csv_private1_uuid = data.private_project1.userstories_csv_uuid - csv_private2_uuid = data.private_project1.userstories_csv_uuid - - users = [ - None, - data.registered_user, - data.project_member_without_perms, - data.project_member_with_perms, - data.project_owner - ] - - results = helper_test_http_method(client, 'get', "{}?uuid={}".format(url, csv_public_uuid), None, users) - assert results == [200, 200, 200, 200, 200] - - results = helper_test_http_method(client, 'get', "{}?uuid={}".format(url, csv_private1_uuid), None, users) - assert results == [200, 200, 200, 200, 200] - - results = helper_test_http_method(client, 'get', "{}?uuid={}".format(url, csv_private2_uuid), None, users) - assert results == [200, 200, 200, 200, 200] - - def test_user_story_action_watch(client, data): public_url = reverse('userstories-watch', kwargs={"pk": data.public_user_story.pk}) private_url1 = reverse('userstories-watch', kwargs={"pk": data.private_user_story1.pk}) @@ -706,3 +848,27 @@ def test_userstory_watchers_retrieve(client, data): assert results == [401, 403, 403, 200, 200] results = helper_test_http_method(client, 'get', blocked_url, None, users) assert results == [401, 403, 403, 200, 200] + + +def test_user_stories_action_csv(client, data): + url = reverse('userstories-csv') + csv_public_uuid = data.public_project.userstories_csv_uuid + csv_private1_uuid = data.private_project1.userstories_csv_uuid + csv_private2_uuid = data.private_project1.userstories_csv_uuid + + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + data.project_owner + ] + + results = helper_test_http_method(client, 'get', "{}?uuid={}".format(url, csv_public_uuid), None, users) + assert results == [200, 200, 200, 200, 200] + + results = helper_test_http_method(client, 'get', "{}?uuid={}".format(url, csv_private1_uuid), None, users) + assert results == [200, 200, 200, 200, 200] + + results = helper_test_http_method(client, 'get', "{}?uuid={}".format(url, csv_private2_uuid), None, users) + assert results == [200, 200, 200, 200, 200] diff --git a/tests/integration/resources_permissions/test_wiki_resources.py b/tests/integration/resources_permissions/test_wiki_resources.py index aaee4e53..e981aa75 100644 --- a/tests/integration/resources_permissions/test_wiki_resources.py +++ b/tests/integration/resources_permissions/test_wiki_resources.py @@ -111,91 +111,9 @@ def data(): return m -def test_wiki_page_retrieve(client, data): - public_url = reverse('wiki-detail', kwargs={"pk": data.public_wiki_page.pk}) - private_url1 = reverse('wiki-detail', kwargs={"pk": data.private_wiki_page1.pk}) - private_url2 = reverse('wiki-detail', kwargs={"pk": data.private_wiki_page2.pk}) - blocked_url = reverse('wiki-detail', kwargs={"pk": data.blocked_wiki_page.pk}) - - users = [ - None, - data.registered_user, - data.project_member_without_perms, - data.project_member_with_perms, - data.project_owner - ] - - results = helper_test_http_method(client, 'get', public_url, None, users) - assert results == [200, 200, 200, 200, 200] - results = helper_test_http_method(client, 'get', private_url1, None, users) - assert results == [200, 200, 200, 200, 200] - results = helper_test_http_method(client, 'get', private_url2, None, users) - assert results == [401, 403, 403, 200, 200] - results = helper_test_http_method(client, 'get', blocked_url, None, users) - assert results == [401, 403, 403, 200, 200] - - -def test_wiki_page_update(client, data): - public_url = reverse('wiki-detail', kwargs={"pk": data.public_wiki_page.pk}) - private_url1 = reverse('wiki-detail', kwargs={"pk": data.private_wiki_page1.pk}) - private_url2 = reverse('wiki-detail', kwargs={"pk": data.private_wiki_page2.pk}) - blocked_url = reverse('wiki-detail', kwargs={"pk": data.blocked_wiki_page.pk}) - - users = [ - None, - data.registered_user, - data.project_member_without_perms, - data.project_member_with_perms, - data.project_owner - ] - - with mock.patch.object(OCCResourceMixin, "_validate_and_update_version"): - wiki_page_data = WikiPageSerializer(data.public_wiki_page).data - wiki_page_data["content"] = "test" - wiki_page_data = json.dumps(wiki_page_data) - results = helper_test_http_method(client, 'put', public_url, wiki_page_data, users) - assert results == [401, 403, 403, 200, 200] - - wiki_page_data = WikiPageSerializer(data.private_wiki_page1).data - wiki_page_data["content"] = "test" - wiki_page_data = json.dumps(wiki_page_data) - results = helper_test_http_method(client, 'put', private_url1, wiki_page_data, users) - assert results == [401, 403, 403, 200, 200] - - wiki_page_data = WikiPageSerializer(data.private_wiki_page2).data - wiki_page_data["content"] = "test" - wiki_page_data = json.dumps(wiki_page_data) - results = helper_test_http_method(client, 'put', private_url2, wiki_page_data, users) - assert results == [401, 403, 403, 200, 200] - - wiki_page_data = WikiPageSerializer(data.blocked_wiki_page).data - wiki_page_data["content"] = "test" - wiki_page_data = json.dumps(wiki_page_data) - results = helper_test_http_method(client, 'put', blocked_url, wiki_page_data, users) - assert results == [401, 403, 403, 451, 451] - - -def test_wiki_page_delete(client, data): - public_url = reverse('wiki-detail', kwargs={"pk": data.public_wiki_page.pk}) - private_url1 = reverse('wiki-detail', kwargs={"pk": data.private_wiki_page1.pk}) - private_url2 = reverse('wiki-detail', kwargs={"pk": data.private_wiki_page2.pk}) - blocked_url = reverse('wiki-detail', kwargs={"pk": data.blocked_wiki_page.pk}) - - users = [ - None, - data.registered_user, - data.project_member_without_perms, - data.project_member_with_perms, - ] - results = helper_test_http_method(client, 'delete', public_url, None, users) - assert results == [401, 403, 403, 204] - results = helper_test_http_method(client, 'delete', private_url1, None, users) - assert results == [401, 403, 403, 204] - results = helper_test_http_method(client, 'delete', private_url2, None, users) - assert results == [401, 403, 403, 204] - results = helper_test_http_method(client, 'delete', blocked_url, None, users) - assert results == [401, 403, 403, 451] - +############################################## +## WIKI PAGES +############################################## def test_wiki_page_list(client, data): url = reverse('wiki-list') @@ -227,6 +145,30 @@ def test_wiki_page_list(client, data): assert response.status_code == 200 +def test_wiki_page_retrieve(client, data): + public_url = reverse('wiki-detail', kwargs={"pk": data.public_wiki_page.pk}) + private_url1 = reverse('wiki-detail', kwargs={"pk": data.private_wiki_page1.pk}) + private_url2 = reverse('wiki-detail', kwargs={"pk": data.private_wiki_page2.pk}) + blocked_url = reverse('wiki-detail', kwargs={"pk": data.blocked_wiki_page.pk}) + + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + data.project_owner + ] + + results = helper_test_http_method(client, 'get', public_url, None, users) + assert results == [200, 200, 200, 200, 200] + results = helper_test_http_method(client, 'get', private_url1, None, users) + assert results == [200, 200, 200, 200, 200] + results = helper_test_http_method(client, 'get', private_url2, None, users) + assert results == [401, 403, 403, 200, 200] + results = helper_test_http_method(client, 'get', blocked_url, None, users) + assert results == [401, 403, 403, 200, 200] + + def test_wiki_page_create(client, data): url = reverse('wiki-list') @@ -270,7 +212,8 @@ def test_wiki_page_create(client, data): results = helper_test_http_method(client, 'post', url, create_data, users, lambda: WikiPage.objects.all().delete()) assert results == [401, 403, 403, 451, 451] -def test_wiki_page_patch(client, data): + +def test_wiki_page_put_update(client, data): public_url = reverse('wiki-detail', kwargs={"pk": data.public_wiki_page.pk}) private_url1 = reverse('wiki-detail', kwargs={"pk": data.private_wiki_page1.pk}) private_url2 = reverse('wiki-detail', kwargs={"pk": data.private_wiki_page2.pk}) @@ -285,68 +228,36 @@ def test_wiki_page_patch(client, data): ] with mock.patch.object(OCCResourceMixin, "_validate_and_update_version"): - patch_data = json.dumps({"content": "test", "version": data.public_wiki_page.version}) - results = helper_test_http_method(client, 'patch', public_url, patch_data, users) - assert results == [401, 403, 403, 200, 200] + wiki_page_data = WikiPageSerializer(data.public_wiki_page).data + wiki_page_data["content"] = "test" + wiki_page_data = json.dumps(wiki_page_data) + results = helper_test_http_method(client, 'put', public_url, wiki_page_data, users) + assert results == [401, 403, 403, 200, 200] - patch_data = json.dumps({"content": "test", "version": data.private_wiki_page2.version}) - results = helper_test_http_method(client, 'patch', private_url1, patch_data, users) - assert results == [401, 403, 403, 200, 200] + wiki_page_data = WikiPageSerializer(data.private_wiki_page1).data + wiki_page_data["content"] = "test" + wiki_page_data = json.dumps(wiki_page_data) + results = helper_test_http_method(client, 'put', private_url1, wiki_page_data, users) + assert results == [401, 403, 403, 200, 200] - patch_data = json.dumps({"content": "test", "version": data.private_wiki_page2.version}) - results = helper_test_http_method(client, 'patch', private_url2, patch_data, users) - assert results == [401, 403, 403, 200, 200] + wiki_page_data = WikiPageSerializer(data.private_wiki_page2).data + wiki_page_data["content"] = "test" + wiki_page_data = json.dumps(wiki_page_data) + results = helper_test_http_method(client, 'put', private_url2, wiki_page_data, users) + assert results == [401, 403, 403, 200, 200] - patch_data = json.dumps({"content": "test", "version": data.blocked_wiki_page.version}) - results = helper_test_http_method(client, 'patch', blocked_url, patch_data, users) - assert results == [401, 403, 403, 451, 451] + wiki_page_data = WikiPageSerializer(data.blocked_wiki_page).data + wiki_page_data["content"] = "test" + wiki_page_data = json.dumps(wiki_page_data) + results = helper_test_http_method(client, 'put', blocked_url, wiki_page_data, users) + assert results == [401, 403, 403, 451, 451] -def test_wiki_page_action_render(client, data): - url = reverse('wiki-render') - - users = [ - None, - data.registered_user, - data.project_member_without_perms, - data.project_member_with_perms, - data.project_owner - ] - - post_data = json.dumps({"content": "test", "project_id": data.public_project.pk}) - results = helper_test_http_method(client, 'post', url, post_data, users) - assert results == [200, 200, 200, 200, 200] - - -def test_wiki_link_retrieve(client, data): - public_url = reverse('wiki-links-detail', kwargs={"pk": data.public_wiki_link.pk}) - private_url1 = reverse('wiki-links-detail', kwargs={"pk": data.private_wiki_link1.pk}) - private_url2 = reverse('wiki-links-detail', kwargs={"pk": data.private_wiki_link2.pk}) - blocked_url = reverse('wiki-links-detail', kwargs={"pk": data.blocked_wiki_link.pk}) - - users = [ - None, - data.registered_user, - data.project_member_without_perms, - data.project_member_with_perms, - data.project_owner - ] - - results = helper_test_http_method(client, 'get', public_url, None, users) - assert results == [200, 200, 200, 200, 200] - results = helper_test_http_method(client, 'get', private_url1, None, users) - assert results == [200, 200, 200, 200, 200] - results = helper_test_http_method(client, 'get', private_url2, None, users) - assert results == [401, 403, 403, 200, 200] - results = helper_test_http_method(client, 'get', blocked_url, None, users) - assert results == [401, 403, 403, 200, 200] - - -def test_wiki_link_update(client, data): - public_url = reverse('wiki-links-detail', kwargs={"pk": data.public_wiki_link.pk}) - private_url1 = reverse('wiki-links-detail', kwargs={"pk": data.private_wiki_link1.pk}) - private_url2 = reverse('wiki-links-detail', kwargs={"pk": data.private_wiki_link2.pk}) - blocked_url = reverse('wiki-links-detail', kwargs={"pk": data.blocked_wiki_link.pk}) +def test_wiki_page_put_comment(client, data): + public_url = reverse('wiki-detail', kwargs={"pk": data.public_wiki_page.pk}) + private_url1 = reverse('wiki-detail', kwargs={"pk": data.private_wiki_page1.pk}) + private_url2 = reverse('wiki-detail', kwargs={"pk": data.private_wiki_page2.pk}) + blocked_url = reverse('wiki-detail', kwargs={"pk": data.blocked_wiki_page.pk}) users = [ None, @@ -357,35 +268,278 @@ def test_wiki_link_update(client, data): ] with mock.patch.object(OCCResourceMixin, "_validate_and_update_version"): - wiki_link_data = WikiLinkSerializer(data.public_wiki_link).data - wiki_link_data["title"] = "test" - wiki_link_data = json.dumps(wiki_link_data) - results = helper_test_http_method(client, 'put', public_url, wiki_link_data, users) - assert results == [401, 403, 403, 200, 200] + wiki_page_data = WikiPageSerializer(data.public_wiki_page).data + wiki_page_data["comment"] = "test comment" + wiki_page_data = json.dumps(wiki_page_data) + results = helper_test_http_method(client, 'put', public_url, wiki_page_data, users) + assert results == [401, 403, 403, 200, 200] - wiki_link_data = WikiLinkSerializer(data.private_wiki_link1).data - wiki_link_data["title"] = "test" - wiki_link_data = json.dumps(wiki_link_data) - results = helper_test_http_method(client, 'put', private_url1, wiki_link_data, users) - assert results == [401, 403, 403, 200, 200] + wiki_page_data = WikiPageSerializer(data.private_wiki_page1).data + wiki_page_data["comment"] = "test comment" + wiki_page_data = json.dumps(wiki_page_data) + results = helper_test_http_method(client, 'put', private_url1, wiki_page_data, users) + assert results == [401, 403, 403, 200, 200] - wiki_link_data = WikiLinkSerializer(data.private_wiki_link2).data - wiki_link_data["title"] = "test" - wiki_link_data = json.dumps(wiki_link_data) - results = helper_test_http_method(client, 'put', private_url2, wiki_link_data, users) - assert results == [401, 403, 403, 200, 200] + wiki_page_data = WikiPageSerializer(data.private_wiki_page2).data + wiki_page_data["comment"] = "test comment" + wiki_page_data = json.dumps(wiki_page_data) + results = helper_test_http_method(client, 'put', private_url2, wiki_page_data, users) + assert results == [401, 403, 403, 200, 200] - wiki_link_data = WikiLinkSerializer(data.blocked_wiki_link).data - wiki_link_data["title"] = "test" - wiki_link_data = json.dumps(wiki_link_data) - results = helper_test_http_method(client, 'put', blocked_url, wiki_link_data, users) - assert results == [401, 403, 403, 451, 451] + wiki_page_data = WikiPageSerializer(data.blocked_wiki_page).data + wiki_page_data["comment"] = "test comment" + wiki_page_data = json.dumps(wiki_page_data) + results = helper_test_http_method(client, 'put', blocked_url, wiki_page_data, users) + assert results == [401, 403, 403, 451, 451] -def test_wiki_link_delete(client, data): - public_url = reverse('wiki-links-detail', kwargs={"pk": data.public_wiki_link.pk}) - private_url1 = reverse('wiki-links-detail', kwargs={"pk": data.private_wiki_link1.pk}) - private_url2 = reverse('wiki-links-detail', kwargs={"pk": data.private_wiki_link2.pk}) - blocked_url = reverse('wiki-links-detail', kwargs={"pk": data.blocked_wiki_link.pk}) + +def test_wiki_page_put_update_and_comment(client, data): + public_url = reverse('wiki-detail', kwargs={"pk": data.public_wiki_page.pk}) + private_url1 = reverse('wiki-detail', kwargs={"pk": data.private_wiki_page1.pk}) + private_url2 = reverse('wiki-detail', kwargs={"pk": data.private_wiki_page2.pk}) + blocked_url = reverse('wiki-detail', kwargs={"pk": data.blocked_wiki_page.pk}) + + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + data.project_owner + ] + + with mock.patch.object(OCCResourceMixin, "_validate_and_update_version"): + wiki_page_data = WikiPageSerializer(data.public_wiki_page).data + wiki_page_data["slug"] = "test" + wiki_page_data["comment"] = "test comment" + wiki_page_data = json.dumps(wiki_page_data) + results = helper_test_http_method(client, 'put', public_url, wiki_page_data, users) + assert results == [401, 403, 403, 200, 200] + + wiki_page_data = WikiPageSerializer(data.private_wiki_page1).data + wiki_page_data["slug"] = "test" + wiki_page_data["comment"] = "test comment" + wiki_page_data = json.dumps(wiki_page_data) + results = helper_test_http_method(client, 'put', private_url1, wiki_page_data, users) + assert results == [401, 403, 403, 200, 200] + + wiki_page_data = WikiPageSerializer(data.private_wiki_page2).data + wiki_page_data["slug"] = "test" + wiki_page_data["comment"] = "test comment" + wiki_page_data = json.dumps(wiki_page_data) + results = helper_test_http_method(client, 'put', private_url2, wiki_page_data, users) + assert results == [401, 403, 403, 200, 200] + + wiki_page_data = WikiPageSerializer(data.blocked_wiki_page).data + wiki_page_data["slug"] = "test" + wiki_page_data["comment"] = "test comment" + wiki_page_data = json.dumps(wiki_page_data) + results = helper_test_http_method(client, 'put', blocked_url, wiki_page_data, users) + assert results == [401, 403, 403, 451, 451] + + +def test_wiki_page_put_update_with_project_change(client): + user1 = f.UserFactory.create() + user2 = f.UserFactory.create() + user3 = f.UserFactory.create() + user4 = f.UserFactory.create() + project1 = f.ProjectFactory() + project2 = f.ProjectFactory() + + membership1 = f.MembershipFactory(project=project1, + user=user1, + role__project=project1, + role__permissions=list(map(lambda x: x[0], MEMBERS_PERMISSIONS))) + membership2 = f.MembershipFactory(project=project2, + user=user1, + role__project=project2, + role__permissions=list(map(lambda x: x[0], MEMBERS_PERMISSIONS))) + membership3 = f.MembershipFactory(project=project1, + user=user2, + role__project=project1, + role__permissions=list(map(lambda x: x[0], MEMBERS_PERMISSIONS))) + membership4 = f.MembershipFactory(project=project2, + user=user3, + role__project=project2, + role__permissions=list(map(lambda x: x[0], MEMBERS_PERMISSIONS))) + + wiki_page = f.WikiPageFactory.create(project=project1) + + url = reverse('wiki-detail', kwargs={"pk": wiki_page.pk}) + + # Test user with permissions in both projects + client.login(user1) + + wiki_page_data = WikiPageSerializer(wiki_page).data + wiki_page_data["project"] = project2.id + wiki_page_data = json.dumps(wiki_page_data) + + response = client.put(url, data=wiki_page_data, content_type="application/json") + + assert response.status_code == 200 + + wiki_page.project = project1 + wiki_page.save() + + # Test user with permissions in only origin project + client.login(user2) + + wiki_page_data = WikiPageSerializer(wiki_page).data + wiki_page_data["project"] = project2.id + wiki_page_data = json.dumps(wiki_page_data) + + response = client.put(url, data=wiki_page_data, content_type="application/json") + + assert response.status_code == 403 + + wiki_page.project = project1 + wiki_page.save() + + # Test user with permissions in only destionation project + client.login(user3) + + wiki_page_data = WikiPageSerializer(wiki_page).data + wiki_page_data["project"] = project2.id + wiki_page_data = json.dumps(wiki_page_data) + + response = client.put(url, data=wiki_page_data, content_type="application/json") + + assert response.status_code == 403 + + wiki_page.project = project1 + wiki_page.save() + + # Test user without permissions in the projects + client.login(user4) + + wiki_page_data = WikiPageSerializer(wiki_page).data + wiki_page_data["project"] = project2.id + wiki_page_data = json.dumps(wiki_page_data) + + response = client.put(url, data=wiki_page_data, content_type="application/json") + + assert response.status_code == 403 + + wiki_page.project = project1 + wiki_page.save() + + +def test_wiki_page_patch_update(client, data): + public_url = reverse('wiki-detail', kwargs={"pk": data.public_wiki_page.pk}) + private_url1 = reverse('wiki-detail', kwargs={"pk": data.private_wiki_page1.pk}) + private_url2 = reverse('wiki-detail', kwargs={"pk": data.private_wiki_page2.pk}) + blocked_url = reverse('wiki-detail', kwargs={"pk": data.blocked_wiki_page.pk}) + + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + data.project_owner + ] + + with mock.patch.object(OCCResourceMixin, "_validate_and_update_version"): + patch_data = json.dumps({"content": "test", "version": data.public_wiki_page.version}) + results = helper_test_http_method(client, 'patch', public_url, patch_data, users) + assert results == [401, 403, 403, 200, 200] + + patch_data = json.dumps({"content": "test", "version": data.private_wiki_page2.version}) + results = helper_test_http_method(client, 'patch', private_url1, patch_data, users) + assert results == [401, 403, 403, 200, 200] + + patch_data = json.dumps({"content": "test", "version": data.private_wiki_page2.version}) + results = helper_test_http_method(client, 'patch', private_url2, patch_data, users) + assert results == [401, 403, 403, 200, 200] + + patch_data = json.dumps({"content": "test", "version": data.blocked_wiki_page.version}) + results = helper_test_http_method(client, 'patch', blocked_url, patch_data, users) + assert results == [401, 403, 403, 451, 451] + + +def test_wiki_page_patch_comment(client, data): + public_url = reverse('wiki-detail', kwargs={"pk": data.public_wiki_page.pk}) + private_url1 = reverse('wiki-detail', kwargs={"pk": data.private_wiki_page1.pk}) + private_url2 = reverse('wiki-detail', kwargs={"pk": data.private_wiki_page2.pk}) + blocked_url = reverse('wiki-detail', kwargs={"pk": data.blocked_wiki_page.pk}) + + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + data.project_owner + ] + + with mock.patch.object(OCCResourceMixin, "_validate_and_update_version"): + patch_data = json.dumps({"comment": "test comment", "version": data.public_wiki_page.version}) + results = helper_test_http_method(client, 'patch', public_url, patch_data, users) + assert results == [401, 403, 403, 200, 200] + + patch_data = json.dumps({"comment": "test comment", "version": data.private_wiki_page2.version}) + results = helper_test_http_method(client, 'patch', private_url1, patch_data, users) + assert results == [401, 403, 403, 200, 200] + + patch_data = json.dumps({"comment": "test comment", "version": data.private_wiki_page2.version}) + results = helper_test_http_method(client, 'patch', private_url2, patch_data, users) + assert results == [401, 403, 403, 200, 200] + + patch_data = json.dumps({"comment": "test comment", "version": data.blocked_wiki_page.version}) + results = helper_test_http_method(client, 'patch', blocked_url, patch_data, users) + assert results == [401, 403, 403, 451, 451] + + +def test_wiki_page_patch_update_and_comment(client, data): + public_url = reverse('wiki-detail', kwargs={"pk": data.public_wiki_page.pk}) + private_url1 = reverse('wiki-detail', kwargs={"pk": data.private_wiki_page1.pk}) + private_url2 = reverse('wiki-detail', kwargs={"pk": data.private_wiki_page2.pk}) + blocked_url = reverse('wiki-detail', kwargs={"pk": data.blocked_wiki_page.pk}) + + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + data.project_owner + ] + + with mock.patch.object(OCCResourceMixin, "_validate_and_update_version"): + patch_data = json.dumps({ + "content": "test", + "comment": "test comment", + "version": data.public_wiki_page.version + }) + results = helper_test_http_method(client, 'patch', public_url, patch_data, users) + assert results == [401, 403, 403, 200, 200] + + patch_data = json.dumps({ + "content": "test", + "comment": "test comment", + "version": data.private_wiki_page2.version + }) + results = helper_test_http_method(client, 'patch', private_url1, patch_data, users) + assert results == [401, 403, 403, 200, 200] + + patch_data = json.dumps({ + "content": "test", + "comment": "test comment", + "version": data.private_wiki_page2.version + }) + results = helper_test_http_method(client, 'patch', private_url2, patch_data, users) + assert results == [401, 403, 403, 200, 200] + + patch_data = json.dumps({ + "content": "test", + "comment": "test comment", + "version": data.blocked_wiki_page.version + }) + results = helper_test_http_method(client, 'patch', blocked_url, patch_data, users) + assert results == [401, 403, 403, 451, 451] + + +def test_wiki_page_delete(client, data): + public_url = reverse('wiki-detail', kwargs={"pk": data.public_wiki_page.pk}) + private_url1 = reverse('wiki-detail', kwargs={"pk": data.private_wiki_page1.pk}) + private_url2 = reverse('wiki-detail', kwargs={"pk": data.private_wiki_page2.pk}) + blocked_url = reverse('wiki-detail', kwargs={"pk": data.blocked_wiki_page.pk}) users = [ None, @@ -403,38 +557,8 @@ def test_wiki_link_delete(client, data): assert results == [401, 403, 403, 451] -def test_wiki_link_list(client, data): - url = reverse('wiki-links-list') - - response = client.get(url) - wiki_links_data = json.loads(response.content.decode('utf-8')) - assert len(wiki_links_data) == 2 - assert response.status_code == 200 - - client.login(data.registered_user) - - response = client.get(url) - wiki_links_data = json.loads(response.content.decode('utf-8')) - assert len(wiki_links_data) == 2 - assert response.status_code == 200 - - client.login(data.project_member_with_perms) - - response = client.get(url) - wiki_links_data = json.loads(response.content.decode('utf-8')) - assert len(wiki_links_data) == 4 - assert response.status_code == 200 - - client.login(data.project_owner) - - response = client.get(url) - wiki_links_data = json.loads(response.content.decode('utf-8')) - assert len(wiki_links_data) == 4 - assert response.status_code == 200 - - -def test_wiki_link_create(client, data): - url = reverse('wiki-links-list') +def test_wiki_page_action_render(client, data): + url = reverse('wiki-render') users = [ None, @@ -444,69 +568,9 @@ def test_wiki_link_create(client, data): data.project_owner ] - create_data = json.dumps({ - "title": "test", - "href": "test", - "project": data.public_project.pk, - }) - results = helper_test_http_method(client, 'post', url, create_data, users, lambda: WikiLink.objects.all().delete()) - assert results == [401, 403, 403, 201, 201] - - create_data = json.dumps({ - "title": "test", - "href": "test", - "project": data.private_project1.pk, - }) - results = helper_test_http_method(client, 'post', url, create_data, users, lambda: WikiLink.objects.all().delete()) - assert results == [401, 403, 403, 201, 201] - - create_data = json.dumps({ - "title": "test", - "href": "test", - "project": data.private_project2.pk, - }) - results = helper_test_http_method(client, 'post', url, create_data, users, lambda: WikiLink.objects.all().delete()) - assert results == [401, 403, 403, 201, 201] - - create_data = json.dumps({ - "title": "test", - "href": "test", - "project": data.blocked_project.pk, - }) - results = helper_test_http_method(client, 'post', url, create_data, users, lambda: WikiLink.objects.all().delete()) - assert results == [401, 403, 403, 451, 451] - - -def test_wiki_link_patch(client, data): - public_url = reverse('wiki-links-detail', kwargs={"pk": data.public_wiki_link.pk}) - private_url1 = reverse('wiki-links-detail', kwargs={"pk": data.private_wiki_link1.pk}) - private_url2 = reverse('wiki-links-detail', kwargs={"pk": data.private_wiki_link2.pk}) - blocked_url = reverse('wiki-links-detail', kwargs={"pk": data.blocked_wiki_link.pk}) - - users = [ - None, - data.registered_user, - data.project_member_without_perms, - data.project_member_with_perms, - data.project_owner - ] - - with mock.patch.object(OCCResourceMixin, "_validate_and_update_version"): - patch_data = json.dumps({"title": "test"}) - results = helper_test_http_method(client, 'patch', public_url, patch_data, users) - assert results == [401, 403, 403, 200, 200] - - patch_data = json.dumps({"title": "test"}) - results = helper_test_http_method(client, 'patch', private_url1, patch_data, users) - assert results == [401, 403, 403, 200, 200] - - patch_data = json.dumps({"title": "test"}) - results = helper_test_http_method(client, 'patch', private_url2, patch_data, users) - assert results == [401, 403, 403, 200, 200] - - patch_data = json.dumps({"title": "test"}) - results = helper_test_http_method(client, 'patch', blocked_url, patch_data, users) - assert results == [401, 403, 403, 451, 451] + post_data = json.dumps({"content": "test", "project_id": data.public_project.pk}) + results = helper_test_http_method(client, 'post', url, post_data, users) + assert results == [200, 200, 200, 200, 200] def test_wikipage_action_watch(client, data): @@ -610,3 +674,199 @@ def test_wikipage_watchers_retrieve(client, data): assert results == [401, 403, 403, 200, 200] results = helper_test_http_method(client, 'get', blocked_url, None, users) assert results == [401, 403, 403, 200, 200] + + +############################################## +## WIKI LINKS +############################################## + +def test_wiki_link_list(client, data): + url = reverse('wiki-links-list') + + response = client.get(url) + wiki_links_data = json.loads(response.content.decode('utf-8')) + assert len(wiki_links_data) == 2 + assert response.status_code == 200 + + client.login(data.registered_user) + + response = client.get(url) + wiki_links_data = json.loads(response.content.decode('utf-8')) + assert len(wiki_links_data) == 2 + assert response.status_code == 200 + + client.login(data.project_member_with_perms) + + response = client.get(url) + wiki_links_data = json.loads(response.content.decode('utf-8')) + assert len(wiki_links_data) == 4 + assert response.status_code == 200 + + client.login(data.project_owner) + + response = client.get(url) + wiki_links_data = json.loads(response.content.decode('utf-8')) + assert len(wiki_links_data) == 4 + assert response.status_code == 200 + + +def test_wiki_link_retrieve(client, data): + public_url = reverse('wiki-links-detail', kwargs={"pk": data.public_wiki_link.pk}) + private_url1 = reverse('wiki-links-detail', kwargs={"pk": data.private_wiki_link1.pk}) + private_url2 = reverse('wiki-links-detail', kwargs={"pk": data.private_wiki_link2.pk}) + blocked_url = reverse('wiki-links-detail', kwargs={"pk": data.blocked_wiki_link.pk}) + + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + data.project_owner + ] + + results = helper_test_http_method(client, 'get', public_url, None, users) + assert results == [200, 200, 200, 200, 200] + results = helper_test_http_method(client, 'get', private_url1, None, users) + assert results == [200, 200, 200, 200, 200] + results = helper_test_http_method(client, 'get', private_url2, None, users) + assert results == [401, 403, 403, 200, 200] + results = helper_test_http_method(client, 'get', blocked_url, None, users) + assert results == [401, 403, 403, 200, 200] + + +def test_wiki_link_create(client, data): + url = reverse('wiki-links-list') + + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + data.project_owner + ] + + create_data = json.dumps({ + "title": "test", + "href": "test", + "project": data.public_project.pk, + }) + results = helper_test_http_method(client, 'post', url, create_data, users, lambda: WikiLink.objects.all().delete()) + assert results == [401, 403, 403, 201, 201] + + create_data = json.dumps({ + "title": "test", + "href": "test", + "project": data.private_project1.pk, + }) + results = helper_test_http_method(client, 'post', url, create_data, users, lambda: WikiLink.objects.all().delete()) + assert results == [401, 403, 403, 201, 201] + + create_data = json.dumps({ + "title": "test", + "href": "test", + "project": data.private_project2.pk, + }) + results = helper_test_http_method(client, 'post', url, create_data, users, lambda: WikiLink.objects.all().delete()) + assert results == [401, 403, 403, 201, 201] + + create_data = json.dumps({ + "title": "test", + "href": "test", + "project": data.blocked_project.pk, + }) + results = helper_test_http_method(client, 'post', url, create_data, users, lambda: WikiLink.objects.all().delete()) + assert results == [401, 403, 403, 451, 451] + + +def test_wiki_link_update(client, data): + public_url = reverse('wiki-links-detail', kwargs={"pk": data.public_wiki_link.pk}) + private_url1 = reverse('wiki-links-detail', kwargs={"pk": data.private_wiki_link1.pk}) + private_url2 = reverse('wiki-links-detail', kwargs={"pk": data.private_wiki_link2.pk}) + blocked_url = reverse('wiki-links-detail', kwargs={"pk": data.blocked_wiki_link.pk}) + + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + data.project_owner + ] + + with mock.patch.object(OCCResourceMixin, "_validate_and_update_version"): + wiki_link_data = WikiLinkSerializer(data.public_wiki_link).data + wiki_link_data["title"] = "test" + wiki_link_data = json.dumps(wiki_link_data) + results = helper_test_http_method(client, 'put', public_url, wiki_link_data, users) + assert results == [401, 403, 403, 200, 200] + + wiki_link_data = WikiLinkSerializer(data.private_wiki_link1).data + wiki_link_data["title"] = "test" + wiki_link_data = json.dumps(wiki_link_data) + results = helper_test_http_method(client, 'put', private_url1, wiki_link_data, users) + assert results == [401, 403, 403, 200, 200] + + wiki_link_data = WikiLinkSerializer(data.private_wiki_link2).data + wiki_link_data["title"] = "test" + wiki_link_data = json.dumps(wiki_link_data) + results = helper_test_http_method(client, 'put', private_url2, wiki_link_data, users) + assert results == [401, 403, 403, 200, 200] + + wiki_link_data = WikiLinkSerializer(data.blocked_wiki_link).data + wiki_link_data["title"] = "test" + wiki_link_data = json.dumps(wiki_link_data) + results = helper_test_http_method(client, 'put', blocked_url, wiki_link_data, users) + assert results == [401, 403, 403, 451, 451] + + +def test_wiki_link_patch(client, data): + public_url = reverse('wiki-links-detail', kwargs={"pk": data.public_wiki_link.pk}) + private_url1 = reverse('wiki-links-detail', kwargs={"pk": data.private_wiki_link1.pk}) + private_url2 = reverse('wiki-links-detail', kwargs={"pk": data.private_wiki_link2.pk}) + blocked_url = reverse('wiki-links-detail', kwargs={"pk": data.blocked_wiki_link.pk}) + + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + data.project_owner + ] + + with mock.patch.object(OCCResourceMixin, "_validate_and_update_version"): + patch_data = json.dumps({"title": "test"}) + results = helper_test_http_method(client, 'patch', public_url, patch_data, users) + assert results == [401, 403, 403, 200, 200] + + patch_data = json.dumps({"title": "test"}) + results = helper_test_http_method(client, 'patch', private_url1, patch_data, users) + assert results == [401, 403, 403, 200, 200] + + patch_data = json.dumps({"title": "test"}) + results = helper_test_http_method(client, 'patch', private_url2, patch_data, users) + assert results == [401, 403, 403, 200, 200] + + patch_data = json.dumps({"title": "test"}) + results = helper_test_http_method(client, 'patch', blocked_url, patch_data, users) + assert results == [401, 403, 403, 451, 451] + + +def test_wiki_link_delete(client, data): + public_url = reverse('wiki-links-detail', kwargs={"pk": data.public_wiki_link.pk}) + private_url1 = reverse('wiki-links-detail', kwargs={"pk": data.private_wiki_link1.pk}) + private_url2 = reverse('wiki-links-detail', kwargs={"pk": data.private_wiki_link2.pk}) + blocked_url = reverse('wiki-links-detail', kwargs={"pk": data.blocked_wiki_link.pk}) + + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + ] + results = helper_test_http_method(client, 'delete', public_url, None, users) + assert results == [401, 403, 403, 204] + results = helper_test_http_method(client, 'delete', private_url1, None, users) + assert results == [401, 403, 403, 204] + results = helper_test_http_method(client, 'delete', private_url2, None, users) + assert results == [401, 403, 403, 204] + results = helper_test_http_method(client, 'delete', blocked_url, None, users) + assert results == [401, 403, 403, 451]