diff --git a/taiga/projects/attachments/migrations/0007_attachment_from_comment.py b/taiga/projects/attachments/migrations/0007_attachment_from_comment.py new file mode 100644 index 00000000..00482ec4 --- /dev/null +++ b/taiga/projects/attachments/migrations/0007_attachment_from_comment.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.4 on 2017-01-17 07:45 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('attachments', '0006_auto_20160617_1233'), + ] + + operations = [ + migrations.AddField( + model_name='attachment', + name='from_comment', + field=models.BooleanField(default=False, verbose_name='from_comment'), + ), + ] diff --git a/taiga/projects/attachments/models.py b/taiga/projects/attachments/models.py index a5110a4b..0e7449d9 100644 --- a/taiga/projects/attachments/models.py +++ b/taiga/projects/attachments/models.py @@ -58,6 +58,7 @@ class Attachment(models.Model): sha1 = models.CharField(default="", max_length=40, verbose_name=_("sha1"), blank=True) is_deprecated = models.BooleanField(default=False, verbose_name=_("is deprecated")) + from_comment = models.BooleanField(default=False, verbose_name=_("from_comment")) description = models.TextField(null=False, blank=True, verbose_name=_("description")) order = models.IntegerField(default=0, null=False, blank=False, verbose_name=_("order")) diff --git a/taiga/projects/attachments/permissions.py b/taiga/projects/attachments/permissions.py index 4c7a7915..d949ade2 100644 --- a/taiga/projects/attachments/permissions.py +++ b/taiga/projects/attachments/permissions.py @@ -27,10 +27,16 @@ class IsAttachmentOwnerPerm(PermissionComponent): return request.user == obj.owner return False +class CommentAttachmentPerm(PermissionComponent): + def check_permissions(self, request, view, obj=None): + if obj.from_comment: + return True + return False + class EpicAttachmentPermission(TaigaResourcePermission): retrieve_perms = HasProjectPerm('view_epics') | IsAttachmentOwnerPerm() - create_perms = HasProjectPerm('modify_epic') + create_perms = HasProjectPerm('modify_epic') | (CommentAttachmentPerm() & HasProjectPerm('comment_epic')) update_perms = HasProjectPerm('modify_epic') | IsAttachmentOwnerPerm() partial_update_perms = HasProjectPerm('modify_epic') | IsAttachmentOwnerPerm() destroy_perms = HasProjectPerm('modify_epic') | IsAttachmentOwnerPerm() @@ -39,7 +45,7 @@ class EpicAttachmentPermission(TaigaResourcePermission): class UserStoryAttachmentPermission(TaigaResourcePermission): retrieve_perms = HasProjectPerm('view_us') | IsAttachmentOwnerPerm() - create_perms = HasProjectPerm('modify_us') + create_perms = HasProjectPerm('modify_us') | (CommentAttachmentPerm() & HasProjectPerm('comment_us')) update_perms = HasProjectPerm('modify_us') | IsAttachmentOwnerPerm() partial_update_perms = HasProjectPerm('modify_us') | IsAttachmentOwnerPerm() destroy_perms = HasProjectPerm('modify_us') | IsAttachmentOwnerPerm() @@ -48,7 +54,7 @@ class UserStoryAttachmentPermission(TaigaResourcePermission): class TaskAttachmentPermission(TaigaResourcePermission): retrieve_perms = HasProjectPerm('view_tasks') | IsAttachmentOwnerPerm() - create_perms = HasProjectPerm('modify_task') + create_perms = HasProjectPerm('modify_task') | (CommentAttachmentPerm() & HasProjectPerm('comment_task')) update_perms = HasProjectPerm('modify_task') | IsAttachmentOwnerPerm() partial_update_perms = HasProjectPerm('modify_task') | IsAttachmentOwnerPerm() destroy_perms = HasProjectPerm('modify_task') | IsAttachmentOwnerPerm() @@ -57,7 +63,7 @@ class TaskAttachmentPermission(TaigaResourcePermission): class IssueAttachmentPermission(TaigaResourcePermission): retrieve_perms = HasProjectPerm('view_issues') | IsAttachmentOwnerPerm() - create_perms = HasProjectPerm('modify_issue') + create_perms = HasProjectPerm('modify_issue') | (CommentAttachmentPerm() & HasProjectPerm('comment_issue')) update_perms = HasProjectPerm('modify_issue') | IsAttachmentOwnerPerm() partial_update_perms = HasProjectPerm('modify_issue') | IsAttachmentOwnerPerm() destroy_perms = HasProjectPerm('modify_issue') | IsAttachmentOwnerPerm() @@ -66,7 +72,7 @@ class IssueAttachmentPermission(TaigaResourcePermission): class WikiAttachmentPermission(TaigaResourcePermission): retrieve_perms = HasProjectPerm('view_wiki_pages') | IsAttachmentOwnerPerm() - create_perms = HasProjectPerm('modify_wiki_page') + create_perms = HasProjectPerm('modify_wiki_page') | (CommentAttachmentPerm() & HasProjectPerm('comment_wiki_page')) update_perms = HasProjectPerm('modify_wiki_page') | IsAttachmentOwnerPerm() partial_update_perms = HasProjectPerm('modify_wiki_page') | IsAttachmentOwnerPerm() destroy_perms = HasProjectPerm('modify_wiki_page') | IsAttachmentOwnerPerm() diff --git a/taiga/projects/attachments/serializers.py b/taiga/projects/attachments/serializers.py index 17176466..398dabdd 100644 --- a/taiga/projects/attachments/serializers.py +++ b/taiga/projects/attachments/serializers.py @@ -35,6 +35,7 @@ class AttachmentSerializer(serializers.LightSerializer): url = Field() description = Field() is_deprecated = Field() + from_comment = Field() created_date = Field() modified_date = Field() object_id = Field() diff --git a/taiga/projects/attachments/validators.py b/taiga/projects/attachments/validators.py index 72355ce4..2ed32e11 100644 --- a/taiga/projects/attachments/validators.py +++ b/taiga/projects/attachments/validators.py @@ -29,5 +29,5 @@ class AttachmentValidator(validators.ModelValidator): model = models.Attachment fields = ("id", "project", "owner", "name", "attached_file", "size", "description", "is_deprecated", "created_date", - "modified_date", "object_id", "order", "sha1") + "modified_date", "object_id", "order", "sha1", "from_comment") read_only_fields = ("owner", "created_date", "modified_date", "sha1") diff --git a/tests/integration/resources_permissions/test_attachment_resources.py b/tests/integration/resources_permissions/test_attachment_resources.py index 456c96f2..02afaa2e 100644 --- a/tests/integration/resources_permissions/test_attachment_resources.py +++ b/tests/integration/resources_permissions/test_attachment_resources.py @@ -1027,3 +1027,67 @@ def test_wiki_attachment_list(client, data, data_wiki): results = helper_test_http_method_and_count(client, 'get', url, None, users) assert results == [(200, 2), (200, 2), (200, 2), (200, 4), (200, 4)] + + +def test_create_attachment_by_external_user_without_comment_permission(client): + issue = f.create_issue() + user = f.UserFactory() + + assert issue.owner != user + assert issue.project.owner != user + + url = reverse("issue-attachments-list") + + data = {"description": "test", + "object_id": issue.pk, + "project": issue.project.id, + "attached_file": SimpleUploadedFile("test.txt", b"test"), + "from_comment": True} + + client.login(user) + response = client.post(url, data) + assert response.status_code == 403 + + +def test_create_attachment_by_external_user_with_comment_permission_but_without_from_comment_flag(client): + project = f.ProjectFactory(public_permissions=['comment_issue']) + issue = f.create_issue(project=project) + + user = f.UserFactory() + + assert issue.owner != user + assert issue.project.owner != user + + url = reverse("issue-attachments-list") + + data = {"description": "test", + "object_id": issue.pk, + "project": issue.project.id, + "attached_file": SimpleUploadedFile("test.txt", b"test"), + "from_comment": False} + + client.login(user) + response = client.post(url, data) + assert response.status_code == 403 + + +def test_create_attachment_by_external_user_with_comment_permission_and_with_from_comment_flag(client): + project = f.ProjectFactory(public_permissions=['comment_issue']) + issue = f.create_issue(project=project) + + user = f.UserFactory() + + assert issue.owner != user + assert issue.project.owner != user + + url = reverse("issue-attachments-list") + + data = {"description": "test", + "object_id": issue.pk, + "project": issue.project.id, + "attached_file": SimpleUploadedFile("test.txt", b"test"), + "from_comment": True} + + client.login(user) + response = client.post(url, data) + assert response.status_code == 201