Refactoring owner permissions, now only is_owner memberships are considered as owner users

remotes/origin/enhancement/email-actions
Alejandro Alonso 2014-11-20 18:34:35 +01:00 committed by Jesús Espino
parent 561dc54b4d
commit c285857844
35 changed files with 307 additions and 56 deletions

View File

@ -39,14 +39,15 @@ def _get_object_project(obj):
def is_project_owner(user, obj):
"""
The owner attribute of a project is just an historical reference
"""
if user.is_superuser:
return True
project = _get_object_project(obj)
if project and project.owner == user:
return True
membership = _get_user_project_membership(user, project)
if membership and membership.is_owner:
return True
@ -80,17 +81,14 @@ def get_user_project_permissions(user, project):
members_permissions = list(map(lambda perm: perm[0], MEMBERS_PERMISSIONS))
public_permissions = list(map(lambda perm: perm[0], USER_PERMISSIONS))
anon_permissions = list(map(lambda perm: perm[0], ANON_PERMISSIONS))
elif project.owner == user:
owner_permissions = list(map(lambda perm: perm[0], OWNERS_PERMISSIONS))
members_permissions = list(map(lambda perm: perm[0], MEMBERS_PERMISSIONS))
public_permissions = project.public_permissions if project.public_permissions is not None else []
anon_permissions = project.anon_permissions if project.anon_permissions is not None else []
elif membership:
if membership.is_owner:
owner_permissions = list(map(lambda perm: perm[0], OWNERS_PERMISSIONS))
members_permissions = list(map(lambda perm: perm[0], MEMBERS_PERMISSIONS))
else:
owner_permissions = []
members_permissions = _get_membership_permissions(membership)
members_permissions = []
members_permissions = members_permissions + _get_membership_permissions(membership)
public_permissions = project.public_permissions if project.public_permissions is not None else []
anon_permissions = project.anon_permissions if project.anon_permissions is not None else []
elif user.is_authenticated():

View File

@ -158,7 +158,7 @@ class ProjectViewSet(ModelCrudViewSet):
def leave(self, request, pk=None):
project = self.get_object()
self.check_permissions(request, 'leave', project)
services.remove_member(project, user=request.user)
services.remove_user_from_project(request.user, project)
return Response(status=status.HTTP_200_OK)
def pre_save(self, obj):
@ -241,6 +241,10 @@ class MembershipViewSet(ModelCrudViewSet):
services.send_invitation(invitation=invitation)
return Response(status=status.HTTP_204_NO_CONTENT)
def pre_delete(self, obj):
if not services.can_user_leave_project(obj.user, obj.project):
raise exc.BadRequest(_("At least one of the user must be an active admin"))
def pre_save(self, obj):
if not obj.token:
obj.token = str(uuid.uuid1())

View File

@ -22,6 +22,7 @@ from taiga.base.api.permissions import (TaigaResourcePermission, HasProjectPerm,
from taiga.base import exceptions as exc
from taiga.projects.models import Membership
from . import services
class CanLeaveProject(PermissionComponent):
def check_permissions(self, request, view, obj=None):
@ -29,14 +30,7 @@ class CanLeaveProject(PermissionComponent):
return False
try:
membership = Membership.objects.get(user=request.user, project=obj)
other_admin_memberships_count = Membership.objects\
.exclude(id=membership.id)\
.filter(project=obj, is_owner=True)\
.count()
# The project need at least one owner
if membership.is_owner and other_admin_memberships_count == 0:
if not services.can_user_leave_project(request.user, obj):
raise exc.PermissionDenied(_("You can't leave the project if there are no more owners"))
return True

View File

@ -30,6 +30,7 @@ from taiga.users.validators import RoleExistsValidator
from taiga.permissions.service import get_user_project_permissions, is_project_owner
from . import models
from . import services
from . validators import ProjectExistsValidator
@ -202,6 +203,17 @@ class MembershipSerializer(ModelSerializer):
return attrs
def validate_is_owner(self, attrs, source):
is_owner = attrs[source]
project = attrs.get("project", None)
if project is None:
project = self.object.project
if self.object and not services.project_has_valid_owners(project, exclude_user=self.object.user):
raise serializers.ValidationError(_("At least one of the user must be an active admin"))
return attrs
class ProjectMembershipSerializer(ModelSerializer):
role_name = serializers.CharField(source='role.name', required=False)

View File

@ -34,7 +34,7 @@ from .stats import get_member_stats_for_project
from .members import create_members_in_bulk
from .members import get_members_from_bulk
from .members import remove_member
from .members import remove_user_from_project, project_has_valid_owners, can_user_leave_project
from .invitations import send_invitation
from .invitations import find_invited_user

View File

@ -32,5 +32,27 @@ def create_members_in_bulk(bulk_data, callback=None, precall=None, **additional_
return members
def remove_member(project, user):
def remove_user_from_project(user, project):
models.Membership.objects.get(project=project, user=user).delete()
def project_has_valid_owners(project, exclude_user=None):
"""
Checks if the project has any owner membership with a user different than the specified
"""
owner_memberships = project.memberships.filter(is_owner=True, user__is_active=True)
if exclude_user:
owner_memberships = owner_memberships.exclude(user=exclude_user)
return owner_memberships.count() > 0
def can_user_leave_project(user, project):
membership = project.memberships.get(user=user)
if not membership.is_owner:
return True
if not project_has_valid_owners(project, exclude_user=user):
return False
return True

View File

@ -40,6 +40,7 @@ from taiga.base.api import ModelCrudViewSet
from taiga.base.utils.slug import slugify_uniquely
from taiga.projects.votes import services as votes_service
from taiga.projects.serializers import StarredSerializer
from taiga.permissions.service import is_project_owner
from . import models
from . import serializers
@ -53,8 +54,8 @@ class MembersFilterBackend(BaseFilterBackend):
if project_id:
Project = apps.get_model('projects', 'Project')
project = get_object_or_404(Project, pk=project_id)
if request.user.is_authenticated() and (project.memberships.filter(user=request.user).exists() or project.owner == request.user):
return queryset.filter(Q(memberships__project=project) | Q(id=project.owner.id)).distinct()
if request.user.is_authenticated() and project.memberships.filter(user=request.user).exists():
return queryset.filter(memberships__project=project).distinct()
else:
raise exc.PermissionDenied(_("You don't have permisions to see this project users."))

View File

@ -69,6 +69,18 @@ def data():
role__project=m.private_project2,
role__permissions=[])
f.MembershipFactory(project=m.public_project,
user=m.project_owner,
is_owner=True)
f.MembershipFactory(project=m.private_project1,
user=m.project_owner,
is_owner=True)
f.MembershipFactory(project=m.private_project2,
user=m.project_owner,
is_owner=True)
return m
@pytest.fixture

View File

@ -56,10 +56,23 @@ def data():
user=m.project_member_with_perms,
role__project=m.private_project2,
role__permissions=list(map(lambda x: x[0], MEMBERS_PERMISSIONS)))
f.MembershipFactory(project=m.private_project2,
user=m.project_member_without_perms,
role__project=m.private_project2,
role__permissions=[])
f.MembershipFactory(project=m.public_project,
user=m.project_owner,
is_owner=True)
f.MembershipFactory(project=m.private_project1,
user=m.project_owner,
is_owner=True)
f.MembershipFactory(project=m.private_project2,
user=m.project_owner,
is_owner=True)
return m
@pytest.fixture

View File

@ -64,6 +64,18 @@ def data():
role__project=m.private_project2,
role__permissions=[])
f.MembershipFactory(project=m.public_project,
user=m.project_owner,
is_owner=True)
f.MembershipFactory(project=m.private_project1,
user=m.project_owner,
is_owner=True)
f.MembershipFactory(project=m.private_project2,
user=m.project_owner,
is_owner=True)
m.public_issue = f.IssueFactory(project=m.public_project,
status__project=m.public_project,
severity__project=m.public_project,

View File

@ -64,6 +64,18 @@ def data():
role__project=m.private_project2,
role__permissions=[])
f.MembershipFactory(project=m.public_project,
user=m.project_owner,
is_owner=True)
f.MembershipFactory(project=m.private_project1,
user=m.project_owner,
is_owner=True)
f.MembershipFactory(project=m.private_project2,
user=m.project_owner,
is_owner=True)
m.public_milestone = f.MilestoneFactory(project=m.public_project)
m.private_milestone1 = f.MilestoneFactory(project=m.private_project1)
m.private_milestone2 = f.MilestoneFactory(project=m.private_project2)

View File

@ -44,6 +44,7 @@ def data():
email=m.project_member_with_perms.email,
role__project=m.private_project1,
role__permissions=list(map(lambda x: x[0], MEMBERS_PERMISSIONS)))
f.MembershipFactory(project=m.private_project1,
user=m.project_member_without_perms,
email=m.project_member_without_perms.email,
@ -60,6 +61,18 @@ def data():
role__project=m.private_project2,
role__permissions=[])
f.MembershipFactory(project=m.public_project,
user=m.project_owner,
is_owner=True)
f.MembershipFactory(project=m.private_project1,
user=m.project_owner,
is_owner=True)
f.MembershipFactory(project=m.private_project2,
user=m.project_owner,
is_owner=True)
m.public_points = f.PointsFactory(project=m.public_project)
m.private_points1 = f.PointsFactory(project=m.private_project1)
m.private_points2 = f.PointsFactory(project=m.private_project2)
@ -1427,31 +1440,31 @@ def test_membership_list(client, data):
response = client.get(url)
projects_data = json.loads(response.content.decode('utf-8'))
assert len(projects_data) == 3
assert len(projects_data) == 5
assert response.status_code == 200
client.login(data.registered_user)
response = client.get(url)
projects_data = json.loads(response.content.decode('utf-8'))
assert len(projects_data) == 3
assert len(projects_data) == 5
assert response.status_code == 200
client.login(data.project_member_without_perms)
response = client.get(url)
projects_data = json.loads(response.content.decode('utf-8'))
assert len(projects_data) == 3
assert len(projects_data) == 5
assert response.status_code == 200
client.login(data.project_member_with_perms)
response = client.get(url)
projects_data = json.loads(response.content.decode('utf-8'))
assert len(projects_data) == 5
assert len(projects_data) == 8
assert response.status_code == 200
client.login(data.project_owner)
response = client.get(url)
projects_data = json.loads(response.content.decode('utf-8'))
assert len(projects_data) == 5
assert len(projects_data) == 8
assert response.status_code == 200

View File

@ -53,6 +53,18 @@ def data():
role__project=m.private_project2,
role__permissions=[])
f.MembershipFactory(project=m.public_project,
user=m.project_owner,
is_owner=True)
f.MembershipFactory(project=m.private_project1,
user=m.project_owner,
is_owner=True)
f.MembershipFactory(project=m.private_project2,
user=m.project_owner,
is_owner=True)
ContentType = apps.get_model("contenttypes", "ContentType")
Project = apps.get_model("projects", "Project")

View File

@ -65,6 +65,18 @@ def data():
role__project=m.private_project2,
role__permissions=[])
f.MembershipFactory(project=m.public_project,
user=m.project_owner,
is_owner=True)
f.MembershipFactory(project=m.private_project1,
user=m.project_owner,
is_owner=True)
f.MembershipFactory(project=m.private_project2,
user=m.project_owner,
is_owner=True)
m.view_only_membership = f.MembershipFactory(project=m.private_project2,
user=m.other_user,
role__project=m.private_project2,

View File

@ -62,6 +62,18 @@ def data():
role__project=m.private_project2,
role__permissions=[])
f.MembershipFactory(project=m.public_project,
user=m.project_owner,
is_owner=True)
f.MembershipFactory(project=m.private_project1,
user=m.project_owner,
is_owner=True)
f.MembershipFactory(project=m.private_project2,
user=m.project_owner,
is_owner=True)
m.public_issue = f.IssueFactory(project=m.public_project,
status__project=m.public_project,
severity__project=m.public_project,

View File

@ -63,6 +63,18 @@ def data():
role__project=m.private_project2,
role__permissions=[])
f.MembershipFactory(project=m.public_project,
user=m.project_owner,
is_owner=True)
f.MembershipFactory(project=m.private_project1,
user=m.project_owner,
is_owner=True)
f.MembershipFactory(project=m.private_project2,
user=m.project_owner,
is_owner=True)
m.public_task = f.TaskFactory(project=m.public_project,
status__project=m.public_project,
milestone__project=m.public_project,

View File

@ -61,6 +61,17 @@ def data():
role__project=m.private_project2,
role__permissions=[])
f.MembershipFactory(project=m.public_project,
user=m.project_owner,
is_owner=True)
f.MembershipFactory(project=m.private_project1,
user=m.project_owner,
is_owner=True)
f.MembershipFactory(project=m.private_project2,
user=m.project_owner,
is_owner=True)
return m

View File

@ -63,6 +63,18 @@ def data():
role__project=m.private_project2,
role__permissions=[])
f.MembershipFactory(project=m.public_project,
user=m.project_owner,
is_owner=True)
f.MembershipFactory(project=m.private_project1,
user=m.project_owner,
is_owner=True)
f.MembershipFactory(project=m.private_project2,
user=m.project_owner,
is_owner=True)
m.public_points = f.PointsFactory(project=m.public_project)
m.private_points1 = f.PointsFactory(project=m.private_project1)
m.private_points2 = f.PointsFactory(project=m.private_project2)

View File

@ -64,6 +64,18 @@ def data():
role__project=m.private_project2,
role__permissions=[])
f.MembershipFactory(project=m.public_project,
user=m.project_owner,
is_owner=True)
f.MembershipFactory(project=m.private_project1,
user=m.project_owner,
is_owner=True)
f.MembershipFactory(project=m.private_project2,
user=m.project_owner,
is_owner=True)
m.public_wiki_page = f.WikiPageFactory(project=m.public_project)
m.private_wiki_page1 = f.WikiPageFactory(project=m.private_project1)
m.private_wiki_page2 = f.WikiPageFactory(project=m.private_project2)

View File

@ -15,6 +15,7 @@ def test_create_user_story_attachment_without_file(client):
Bug test "Don't create attachments without attached_file"
"""
us = f.UserStoryFactory.create()
membership = f.MembershipFactory(project=us.project, user=us.owner, is_owner=True)
attachment_data = {
"description": "test",
"attached_file": None,
@ -31,6 +32,7 @@ def test_create_user_story_attachment_without_file(client):
def test_create_attachment_on_wrong_project(client):
issue1 = f.create_issue()
issue2 = f.create_issue(owner=issue1.owner)
membership = f.MembershipFactory(project=issue1.project, user=issue1.owner, is_owner=True)
assert issue1.owner == issue2.owner
assert issue1.project.owner == issue2.project.owner

View File

@ -399,6 +399,7 @@ def test_issues_event_bad_comment(client):
def test_api_get_project_modules(client):
project = f.create_project()
membership = f.MembershipFactory(project=project, user=project.owner, is_owner=True)
url = reverse("projects-modules", args=(project.id,))
@ -413,6 +414,7 @@ def test_api_get_project_modules(client):
def test_api_patch_project_modules(client):
project = f.create_project()
membership = f.MembershipFactory(project=project, user=project.owner, is_owner=True)
url = reverse("projects-modules", args=(project.id,))

View File

@ -143,7 +143,7 @@ def test_issue_resource_history_test(client):
user = f.UserFactory.create()
project = f.ProjectFactory.create(owner=user)
role = f.RoleFactory.create(project=project)
member = f.MembershipFactory.create(project=project, user=user, role=role)
membership = f.MembershipFactory.create(project=project, user=user, role=role, is_owner=True)
issue = f.IssueFactory.create(owner=user, project=project)
mock_path = "taiga.projects.issues.api.IssueViewSet.pre_conditions_on_save"
@ -200,6 +200,7 @@ def test_take_hidden_snapshot():
def test_history_with_only_comment_shouldnot_be_hidden(client):
project = f.create_project()
us = f.create_userstory(project=project)
membership = f.MembershipFactory.create(project=project, user=project.owner, is_owner=True)
qs_all = HistoryEntry.objects.all()
qs_hidden = qs_all.filter(is_hidden=True)
@ -209,11 +210,9 @@ def test_history_with_only_comment_shouldnot_be_hidden(client):
url = reverse("userstories-detail", args=[us.pk])
data = json.dumps({"comment": "test comment", "version": us.version})
print(url, data)
client.login(project.owner)
response = client.patch(url, data, content_type="application/json")
assert response.status_code == 200, response.content
assert qs_all.count() == 1
assert qs_hidden.count() == 0

View File

@ -168,6 +168,7 @@ def test_invalid_project_import_with_extra_data(client):
def test_invalid_issue_import(client):
user = f.UserFactory.create()
project = f.ProjectFactory.create(owner=user)
membership = f.MembershipFactory(project=project, user=user, is_owner=True)
client.login(user)
url = reverse("importer-issue", args=[project.pk])
@ -179,6 +180,7 @@ def test_invalid_issue_import(client):
def test_valid_user_story_import(client):
user = f.UserFactory.create()
project = f.ProjectFactory.create(owner=user)
membership = f.MembershipFactory(project=project, user=user, is_owner=True)
project.default_us_status = f.UserStoryStatusFactory.create(project=project)
project.save()
client.login(user)
@ -199,6 +201,7 @@ def test_valid_user_story_import(client):
def test_valid_issue_import_without_extra_data(client):
user = f.UserFactory.create()
project = f.ProjectFactory.create(owner=user)
membership = f.MembershipFactory(project=project, user=user, is_owner=True)
project.default_issue_type = f.IssueTypeFactory.create(project=project)
project.default_issue_status = f.IssueStatusFactory.create(project=project)
project.default_severity = f.SeverityFactory.create(project=project)
@ -220,6 +223,7 @@ def test_valid_issue_import_without_extra_data(client):
def test_valid_issue_import_with_extra_data(client):
user = f.UserFactory.create()
project = f.ProjectFactory.create(owner=user)
membership = f.MembershipFactory(project=project, user=user, is_owner=True)
project.default_issue_type = f.IssueTypeFactory.create(project=project)
project.default_issue_status = f.IssueStatusFactory.create(project=project)
project.default_severity = f.SeverityFactory.create(project=project)
@ -252,6 +256,7 @@ def test_valid_issue_import_with_extra_data(client):
def test_invalid_issue_import_with_extra_data(client):
user = f.UserFactory.create()
project = f.ProjectFactory.create(owner=user)
membership = f.MembershipFactory(project=project, user=user, is_owner=True)
project.default_issue_type = f.IssueTypeFactory.create(project=project)
project.default_issue_status = f.IssueStatusFactory.create(project=project)
project.default_severity = f.SeverityFactory.create(project=project)
@ -275,6 +280,7 @@ def test_invalid_issue_import_with_extra_data(client):
def test_invalid_issue_import_with_bad_choices(client):
user = f.UserFactory.create()
project = f.ProjectFactory.create(owner=user)
membership = f.MembershipFactory(project=project, user=user, is_owner=True)
project.default_issue_type = f.IssueTypeFactory.create(project=project)
project.default_issue_status = f.IssueStatusFactory.create(project=project)
project.default_severity = f.SeverityFactory.create(project=project)
@ -333,6 +339,7 @@ def test_invalid_issue_import_with_bad_choices(client):
def test_invalid_us_import(client):
user = f.UserFactory.create()
project = f.ProjectFactory.create(owner=user)
membership = f.MembershipFactory(project=project, user=user, is_owner=True)
client.login(user)
url = reverse("importer-us", args=[project.pk])
@ -344,6 +351,7 @@ def test_invalid_us_import(client):
def test_valid_us_import_without_extra_data(client):
user = f.UserFactory.create()
project = f.ProjectFactory.create(owner=user)
membership = f.MembershipFactory(project=project, user=user, is_owner=True)
project.default_us_status = f.UserStoryStatusFactory.create(project=project)
project.save()
client.login(user)
@ -362,6 +370,7 @@ def test_valid_us_import_without_extra_data(client):
def test_valid_us_import_with_extra_data(client):
user = f.UserFactory.create()
project = f.ProjectFactory.create(owner=user)
membership = f.MembershipFactory(project=project, user=user, is_owner=True)
project.default_us_status = f.UserStoryStatusFactory.create(project=project)
project.save()
client.login(user)
@ -389,6 +398,7 @@ def test_valid_us_import_with_extra_data(client):
def test_invalid_us_import_with_extra_data(client):
user = f.UserFactory.create()
project = f.ProjectFactory.create(owner=user)
membership = f.MembershipFactory(project=project, user=user, is_owner=True)
project.default_us_status = f.UserStoryStatusFactory.create(project=project)
project.save()
client.login(user)
@ -409,6 +419,7 @@ def test_invalid_us_import_with_extra_data(client):
def test_invalid_us_import_with_bad_choices(client):
user = f.UserFactory.create()
project = f.ProjectFactory.create(owner=user)
membership = f.MembershipFactory(project=project, user=user, is_owner=True)
project.default_us_status = f.UserStoryStatusFactory.create(project=project)
project.save()
client.login(user)
@ -428,6 +439,7 @@ def test_invalid_us_import_with_bad_choices(client):
def test_invalid_task_import(client):
user = f.UserFactory.create()
project = f.ProjectFactory.create(owner=user)
membership = f.MembershipFactory(project=project, user=user, is_owner=True)
client.login(user)
url = reverse("importer-task", args=[project.pk])
@ -439,6 +451,7 @@ def test_invalid_task_import(client):
def test_valid_task_import_without_extra_data(client):
user = f.UserFactory.create()
project = f.ProjectFactory.create(owner=user)
membership = f.MembershipFactory(project=project, user=user, is_owner=True)
project.default_task_status = f.TaskStatusFactory.create(project=project)
project.save()
client.login(user)
@ -457,6 +470,7 @@ def test_valid_task_import_without_extra_data(client):
def test_valid_task_import_with_extra_data(client):
user = f.UserFactory.create()
project = f.ProjectFactory.create(owner=user)
membership = f.MembershipFactory(project=project, user=user, is_owner=True)
project.default_task_status = f.TaskStatusFactory.create(project=project)
project.save()
client.login(user)
@ -484,6 +498,7 @@ def test_valid_task_import_with_extra_data(client):
def test_invalid_task_import_with_extra_data(client):
user = f.UserFactory.create()
project = f.ProjectFactory.create(owner=user)
membership = f.MembershipFactory(project=project, user=user, is_owner=True)
project.default_task_status = f.TaskStatusFactory.create(project=project)
project.save()
client.login(user)
@ -504,6 +519,7 @@ def test_invalid_task_import_with_extra_data(client):
def test_invalid_task_import_with_bad_choices(client):
user = f.UserFactory.create()
project = f.ProjectFactory.create(owner=user)
membership = f.MembershipFactory(project=project, user=user, is_owner=True)
project.default_task_status = f.TaskStatusFactory.create(project=project)
project.save()
client.login(user)
@ -523,6 +539,7 @@ def test_invalid_task_import_with_bad_choices(client):
def test_valid_task_with_user_story(client):
user = f.UserFactory.create()
project = f.ProjectFactory.create(owner=user)
membership = f.MembershipFactory(project=project, user=user, is_owner=True)
project.default_task_status = f.TaskStatusFactory.create(project=project)
us = f.UserStoryFactory.create(project=project)
project.save()
@ -542,6 +559,7 @@ def test_valid_task_with_user_story(client):
def test_invalid_wiki_page_import(client):
user = f.UserFactory.create()
project = f.ProjectFactory.create(owner=user)
membership = f.MembershipFactory(project=project, user=user, is_owner=True)
client.login(user)
url = reverse("importer-wiki-page", args=[project.pk])
@ -553,6 +571,7 @@ def test_invalid_wiki_page_import(client):
def test_valid_wiki_page_import_without_extra_data(client):
user = f.UserFactory.create()
project = f.ProjectFactory.create(owner=user)
membership = f.MembershipFactory(project=project, user=user, is_owner=True)
client.login(user)
url = reverse("importer-wiki-page", args=[project.pk])
@ -568,6 +587,7 @@ def test_valid_wiki_page_import_without_extra_data(client):
def test_valid_wiki_page_import_with_extra_data(client):
user = f.UserFactory.create()
project = f.ProjectFactory.create(owner=user)
membership = f.MembershipFactory(project=project, user=user, is_owner=True)
client.login(user)
url = reverse("importer-wiki-page", args=[project.pk])
@ -592,6 +612,7 @@ def test_valid_wiki_page_import_with_extra_data(client):
def test_invalid_wiki_page_import_with_extra_data(client):
user = f.UserFactory.create()
project = f.ProjectFactory.create(owner=user)
membership = f.MembershipFactory(project=project, user=user, is_owner=True)
client.login(user)
url = reverse("importer-wiki-page", args=[project.pk])
@ -610,6 +631,7 @@ def test_invalid_wiki_page_import_with_extra_data(client):
def test_invalid_wiki_link_import(client):
user = f.UserFactory.create()
project = f.ProjectFactory.create(owner=user)
membership = f.MembershipFactory(project=project, user=user, is_owner=True)
client.login(user)
url = reverse("importer-wiki-link", args=[project.pk])
@ -621,6 +643,7 @@ def test_invalid_wiki_link_import(client):
def test_valid_wiki_link_import(client):
user = f.UserFactory.create()
project = f.ProjectFactory.create(owner=user)
membership = f.MembershipFactory(project=project, user=user, is_owner=True)
client.login(user)
url = reverse("importer-wiki-link", args=[project.pk])
@ -636,6 +659,7 @@ def test_valid_wiki_link_import(client):
def test_invalid_milestone_import(client):
user = f.UserFactory.create()
project = f.ProjectFactory.create(owner=user)
membership = f.MembershipFactory(project=project, user=user, is_owner=True)
client.login(user)
url = reverse("importer-milestone", args=[project.pk])
@ -647,6 +671,7 @@ def test_invalid_milestone_import(client):
def test_valid_milestone_import(client):
user = f.UserFactory.create()
project = f.ProjectFactory.create(owner=user)
membership = f.MembershipFactory(project=project, user=user, is_owner=True)
client.login(user)
url = reverse("importer-milestone", args=[project.pk])
@ -663,6 +688,7 @@ def test_valid_milestone_import(client):
def test_milestone_import_duplicated_milestone(client):
user = f.UserFactory.create()
project = f.ProjectFactory.create(owner=user)
membership = f.MembershipFactory(project=project, user=user, is_owner=True)
client.login(user)
url = reverse("importer-milestone", args=[project.pk])

View File

@ -46,6 +46,7 @@ def test_update_issues_order_in_bulk():
def test_api_create_issues_in_bulk(client):
project = f.create_project()
membership = f.MembershipFactory(project=project, user=project.owner, is_owner=True)
url = reverse("issues-bulk-create")

View File

@ -34,6 +34,7 @@ def test_api_create_bulk_members(client):
joseph = f.UserFactory.create()
tester = f.RoleFactory(project=project, name="Tester")
gamer = f.RoleFactory(project=project, name="Gamer")
membership = f.MembershipFactory(project=project, user=project.owner, is_owner=True)
url = reverse("memberships-bulk-create")
@ -54,6 +55,7 @@ def test_api_create_bulk_members(client):
def test_api_create_bulk_members_with_extra_text(client, outbox):
project = f.ProjectFactory()
tester = f.RoleFactory(project=project, name="Tester")
membership = f.MembershipFactory(project=project, user=project.owner, is_owner=True)
url = reverse("memberships-bulk-create")
invitation_extra_text = "this is a not so random invitation text"
@ -77,6 +79,7 @@ def test_api_create_bulk_members_with_extra_text(client, outbox):
def test_api_resend_invitation(client, outbox):
invitation = f.create_invitation()
membership = f.MembershipFactory(project=invitation.project, user=invitation.project.owner, is_owner=True)
url = reverse("memberships-resend-invitation", kwargs={"pk": invitation.pk})
client.login(invitation.project.owner)
@ -91,6 +94,7 @@ def test_api_invite_existing_user(client, outbox):
"Should create the invitation linked to that user"
user = f.UserFactory.create()
role = f.RoleFactory.create()
membership = f.MembershipFactory(project=role.project, user=role.project.owner, is_owner=True)
client.login(role.project.owner)

View File

@ -33,9 +33,8 @@ def test_update_milestone_with_userstories_list(client):
user = f.UserFactory.create()
project = f.ProjectFactory.create(owner=user)
role = f.RoleFactory.create(project=project)
member = f.MembershipFactory.create(project=project, user=user, role=role)
member = f.MembershipFactory.create(project=project, user=user, role=role, is_owner=True)
sprint = f.MilestoneFactory.create(project=project, owner=user)
points = f.PointsFactory.create(project=project, value=None)
us = f.UserStoryFactory.create(project=project, owner=user)
@ -49,4 +48,3 @@ def test_update_milestone_with_userstories_list(client):
client.login(user)
response = client.json.patch(url, json.dumps(form_data))
assert response.status_code == 200

View File

@ -230,7 +230,7 @@ def test_resource_notification_test(client, mail):
user2 = f.UserFactory.create()
project = f.ProjectFactory.create(owner=user1)
role = f.RoleFactory.create(project=project)
member1 = f.MembershipFactory.create(project=project, user=user1, role=role)
member1 = f.MembershipFactory.create(project=project, user=user1, role=role, is_owner=True)
member2 = f.MembershipFactory.create(project=project, user=user2, role=role)
issue = f.IssueFactory.create(owner=user2, project=project)
@ -268,7 +268,7 @@ def test_watchers_assignation_for_issue(client):
project2 = f.ProjectFactory.create(owner=user2)
role1 = f.RoleFactory.create(project=project1)
role2 = f.RoleFactory.create(project=project2)
member1 = f.MembershipFactory.create(project=project1, user=user1, role=role1)
member1 = f.MembershipFactory.create(project=project1, user=user1, role=role1, is_owner=True)
member2 = f.MembershipFactory.create(project=project2, user=user2, role=role2)
client.login(user1)
@ -321,7 +321,7 @@ def test_watchers_assignation_for_task(client):
project2 = f.ProjectFactory.create(owner=user2)
role1 = f.RoleFactory.create(project=project1)
role2 = f.RoleFactory.create(project=project2)
member1 = f.MembershipFactory.create(project=project1, user=user1, role=role1)
member1 = f.MembershipFactory.create(project=project1, user=user1, role=role1, is_owner=True)
member2 = f.MembershipFactory.create(project=project2, user=user2, role=role2)
client.login(user1)
@ -374,7 +374,7 @@ def test_watchers_assignation_for_us(client):
project2 = f.ProjectFactory.create(owner=user2)
role1 = f.RoleFactory.create(project=project1)
role2 = f.RoleFactory.create(project=project2)
member1 = f.MembershipFactory.create(project=project1, user=user1, role=role1)
member1 = f.MembershipFactory.create(project=project1, user=user1, role=role1, is_owner=True)
member2 = f.MembershipFactory.create(project=project2, user=user2, role=role2)
client.login(user1)

View File

@ -33,7 +33,7 @@ pytestmark = pytest.mark.django_db
def test_invalid_concurrent_save_for_issue(client):
user = f.UserFactory.create()
project = f.ProjectFactory.create(owner=user)
membership = f.MembershipFactory.create(project=project, user=user)
membership = f.MembershipFactory.create(project=project, user=user, is_owner=True)
issue = f.IssueFactory.create(version=10, project=project)
client.login(user)
@ -48,7 +48,7 @@ def test_invalid_concurrent_save_for_issue(client):
def test_valid_concurrent_save_for_issue(client):
user = f.UserFactory.create()
project = f.ProjectFactory.create(owner=user)
membership = f.MembershipFactory.create(project=project, user=user)
membership = f.MembershipFactory.create(project=project, user=user, is_owner=True)
issue = f.IssueFactory.create(version=10, project=project)
client.login(user)
@ -66,7 +66,7 @@ def test_valid_concurrent_save_for_issue(client):
def test_invalid_concurrent_save_for_wiki_page(client):
user = f.UserFactory.create()
project = f.ProjectFactory.create(owner=user)
membership = f.MembershipFactory.create(project=project, user=user)
membership = f.MembershipFactory.create(project=project, user=user, is_owner=True)
wiki_page = f.WikiPageFactory.create(version=10, project=project, owner=user)
client.login(user)
@ -78,7 +78,7 @@ def test_invalid_concurrent_save_for_wiki_page(client):
def test_valid_concurrent_save_for_wiki_page(client):
user = f.UserFactory.create()
project = f.ProjectFactory.create(owner=user)
membership = f.MembershipFactory.create(project=project, user=user)
membership = f.MembershipFactory.create(project=project, user=user, is_owner=True)
wiki_page = f.WikiPageFactory.create(version=10, project=project, owner=user)
client.login(user)
@ -93,7 +93,7 @@ def test_valid_concurrent_save_for_wiki_page(client):
def test_invalid_concurrent_save_for_us(client):
user = f.UserFactory.create()
project = f.ProjectFactory.create(owner=user)
membership = f.MembershipFactory.create(project=project, user=user)
membership = f.MembershipFactory.create(project=project, user=user, is_owner=True)
userstory = f.UserStoryFactory.create(version=10, project=project)
client.login(user)
@ -105,7 +105,7 @@ def test_invalid_concurrent_save_for_us(client):
def test_valid_us_creation(client):
user = f.UserFactory.create()
project = f.ProjectFactory.create(owner=user)
membership = f.MembershipFactory.create(project=project, user=user)
membership = f.MembershipFactory.create(project=project, user=user, is_owner=True)
client.login(user)
@ -121,7 +121,7 @@ def test_valid_us_creation(client):
def test_valid_concurrent_save_for_us(client):
user = f.UserFactory.create()
project = f.ProjectFactory.create(owner=user)
membership = f.MembershipFactory.create(project=project, user=user)
membership = f.MembershipFactory.create(project=project, user=user, is_owner=True)
userstory = f.UserStoryFactory.create(version=10, project=project)
client.login(user)
@ -136,7 +136,7 @@ def test_valid_concurrent_save_for_us(client):
def test_invalid_concurrent_save_for_task(client):
user = f.UserFactory.create()
project = f.ProjectFactory.create(owner=user)
membership = f.MembershipFactory.create(project=project, user=user)
membership = f.MembershipFactory.create(project=project, user=user, is_owner=True)
task = f.TaskFactory.create(version=10, project=project)
client.login(user)
@ -150,7 +150,7 @@ def test_invalid_concurrent_save_for_task(client):
def test_valid_concurrent_save_for_task(client):
user = f.UserFactory.create()
project = f.ProjectFactory.create(owner=user)
membership = f.MembershipFactory.create(project=project, user=user)
membership = f.MembershipFactory.create(project=project, user=user, is_owner=True)
task = f.TaskFactory.create(version=10, project=project)
client.login(user)

View File

@ -53,9 +53,7 @@ def test_owner_get_user_project_permissions():
factories.MembershipFactory(user=user1, project=project, role=role)
expected_perms = set(
["test1", "test2"] +
[x[0] for x in permissions.OWNERS_PERMISSIONS] +
[x[0] for x in permissions.MEMBERS_PERMISSIONS]
["test1", "test2", "view_us"]
)
assert service.get_user_project_permissions(user1, project) == expected_perms
@ -70,7 +68,8 @@ def test_owner_member_get_user_project_permissions():
expected_perms = set(
["test1", "test2", "test3"] +
[x[0] for x in permissions.OWNERS_PERMISSIONS]
[x[0] for x in permissions.OWNERS_PERMISSIONS] +
[x[0] for x in permissions.MEMBERS_PERMISSIONS]
)
assert service.get_user_project_permissions(user1, project) == expected_perms

View File

@ -22,6 +22,7 @@ def test_create_project(client):
def test_partially_update_project(client):
project = f.create_project()
f.MembershipFactory(user=project.owner, project=project, is_owner=True)
url = reverse("projects-detail", kwargs={"pk": project.pk})
data = {"name": ""}
@ -32,6 +33,7 @@ def test_partially_update_project(client):
def test_us_status_slug_generation(client):
us_status = f.UserStoryStatusFactory(name="NEW")
f.MembershipFactory(user=us_status.project.owner, project=us_status.project, is_owner=True)
assert us_status.slug == "new"
client.login(us_status.project.owner)
@ -51,6 +53,7 @@ def test_us_status_slug_generation(client):
def test_task_status_slug_generation(client):
task_status = f.TaskStatusFactory(name="NEW")
f.MembershipFactory(user=task_status.project.owner, project=task_status.project, is_owner=True)
assert task_status.slug == "new"
client.login(task_status.project.owner)
@ -70,6 +73,7 @@ def test_task_status_slug_generation(client):
def test_issue_status_slug_generation(client):
issue_status = f.IssueStatusFactory(name="NEW")
f.MembershipFactory(user=issue_status.project.owner, project=issue_status.project, is_owner=True)
assert issue_status.slug == "new"
client.login(issue_status.project.owner)
@ -90,7 +94,7 @@ def test_issue_status_slug_generation(client):
def test_points_name_duplicated(client):
point_1 = f.PointsFactory()
point_2 = f.PointsFactory(project=point_1.project)
f.MembershipFactory(user=point_1.project.owner, project=point_1.project, is_owner=True)
client.login(point_1.project.owner)
url = reverse("points-detail", kwargs={"pk": point_2.pk})
@ -192,3 +196,30 @@ def test_leave_project_invalid_membership(client):
url = reverse("projects-leave", args=(project.id,))
response = client.post(url)
assert response.status_code == 404
def test_delete_membership_only_owner(client):
user = f.UserFactory.create()
project = f.ProjectFactory.create()
role = f.RoleFactory.create(project=project, permissions=["view_project"])
membership = f.MembershipFactory.create(project=project, user=user, role=role, is_owner=True)
client.login(user)
url = reverse("memberships-detail", args=(membership.id,))
response = client.delete(url)
assert response.status_code == 400
assert json.loads(response.content)["_error_message"] == "At least one of the user must be an active admin"
def test_edit_membership_only_owner(client):
user = f.UserFactory.create()
project = f.ProjectFactory.create()
role = f.RoleFactory.create(project=project, permissions=["view_project"])
membership = f.MembershipFactory.create(project=project, user=user, role=role, is_owner=True)
data = {
"is_owner": False
}
client.login(user)
url = reverse("memberships-detail", args=(membership.id,))
response = client.json.patch(url, json.dumps(data))
assert response.status_code == 400
assert response.data["is_owner"][0] == "At least one of the user must be an active admin"

View File

@ -39,7 +39,7 @@ def test_destroy_role_and_reassign_members(client):
project = f.ProjectFactory.create(owner=user1)
role1 = f.RoleFactory.create(project=project)
role2 = f.RoleFactory.create(project=project)
member = f.MembershipFactory.create(project=project, user=user1, role=role1)
member = f.MembershipFactory.create(project=project, user=user1, role=role1, is_owner=True)
member = f.MembershipFactory.create(project=project, user=user2, role=role2)
url = reverse("roles-detail", args=[role2.pk]) + "?moveTo={}".format(role1.pk)

View File

@ -74,6 +74,7 @@ def test_project_member_unstar_project(client):
def test_list_project_fans(client):
user = f.UserFactory.create()
project = f.ProjectFactory.create(owner=user)
f.MembershipFactory.create(project=project, user=user, is_owner=True)
fan = f.VoteFactory.create(content_object=project)
url = reverse("projects-fans", args=(project.id,))
@ -100,6 +101,7 @@ def test_list_user_starred_projects(client):
def test_get_project_stars(client):
user = f.UserFactory.create()
project = f.ProjectFactory.create(owner=user)
f.MembershipFactory.create(project=project, user=user, is_owner=True)
url = reverse("projects-detail", args=(project.id,))
f.VotesFactory.create(content_object=project, count=5)
f.VotesFactory.create(count=3)

View File

@ -36,6 +36,7 @@ Task #2
def test_api_update_task_tags(client):
task = f.create_task()
f.MembershipFactory.create(project=task.project, user=task.owner, is_owner=True)
url = reverse("tasks-detail", kwargs={"pk": task.pk})
data = {"tags": ["back", "front"], "version": task.version}
@ -47,6 +48,7 @@ def test_api_update_task_tags(client):
def test_api_create_in_bulk_with_status(client):
us = f.create_userstory()
f.MembershipFactory.create(project=us.project, user=us.owner, is_owner=True)
us.project.default_task_status = f.TaskStatusFactory.create(project=us.project)
url = reverse("tasks-bulk-create")
data = {
@ -69,6 +71,7 @@ def test_api_create_invalid_task(client):
# But the User Story is not associated with the milestone
us_milestone = f.MilestoneFactory.create()
us = f.create_userstory(milestone=us_milestone)
f.MembershipFactory.create(project=us.project, user=us.owner, is_owner=True)
us.project.default_task_status = f.TaskStatusFactory.create(project=us.project)
task_milestone = f.MilestoneFactory.create(project=us.project, owner=us.owner)

View File

@ -44,6 +44,7 @@ def test_update_userstories_order_in_bulk():
def test_api_delete_userstory(client):
us = f.create_userstory()
f.MembershipFactory.create(project=us.project, user=us.owner, is_owner=True)
url = reverse("userstories-detail", kwargs={"pk": us.pk})
client.login(us.owner)
@ -67,6 +68,7 @@ def test_api_filter_by_subject(client):
def test_api_create_in_bulk_with_status(client):
project = f.create_project()
f.MembershipFactory.create(project=project, user=project.owner, is_owner=True)
url = reverse("userstories-bulk-create")
data = {
"bulk_stories": "Story #1\nStory #2",
@ -83,6 +85,7 @@ def test_api_create_in_bulk_with_status(client):
def test_api_update_backlog_order_in_bulk(client):
project = f.create_project()
f.MembershipFactory.create(project=project, user=project.owner, is_owner=True)
us1 = f.create_userstory(project=project)
us2 = f.create_userstory(project=project)
@ -102,9 +105,9 @@ def test_api_update_backlog_order_in_bulk(client):
response2 = client.json.post(url2, json.dumps(data))
response3 = client.json.post(url3, json.dumps(data))
assert response1.status_code == 204, response.data
assert response2.status_code == 204, response.data
assert response3.status_code == 204, response.data
assert response1.status_code == 204, response1.data
assert response2.status_code == 204, response2.data
assert response3.status_code == 204, response3.data
from taiga.projects.userstories.serializers import UserStorySerializer
@ -118,7 +121,7 @@ def test_update_userstory_points(client):
role1 = f.RoleFactory.create(project=project)
role2 = f.RoleFactory.create(project=project)
member = f.MembershipFactory.create(project=project, user=user1, role=role1)
member = f.MembershipFactory.create(project=project, user=user1, role=role1, is_owner=True)
member = f.MembershipFactory.create(project=project, user=user2, role=role2)
points1 = f.PointsFactory.create(project=project, value=None)

View File

@ -27,6 +27,7 @@ pytestmark = pytest.mark.django_db
def test_upvote_issue(client):
user = f.UserFactory.create()
issue = f.create_issue(owner=user)
f.MembershipFactory.create(project=issue.project, user=user, is_owner=True)
url = reverse("issues-upvote", args=(issue.id,))
client.login(user)
@ -38,6 +39,7 @@ def test_upvote_issue(client):
def test_downvote_issue(client):
user = f.UserFactory.create()
issue = f.create_issue(owner=user)
f.MembershipFactory.create(project=issue.project, user=user, is_owner=True)
url = reverse("issues-downvote", args=(issue.id,))
client.login(user)
@ -49,6 +51,7 @@ def test_downvote_issue(client):
def test_list_issue_voters(client):
user = f.UserFactory.create()
issue = f.create_issue(owner=user)
f.MembershipFactory.create(project=issue.project, user=user, is_owner=True)
url = reverse("issue-voters-list", args=(issue.id,))
f.VoteFactory.create(content_object=issue, user=user)
@ -62,6 +65,7 @@ def test_list_issue_voters(client):
def test_get_issue_voter(client):
user = f.UserFactory.create()
issue = f.create_issue(owner=user)
f.MembershipFactory.create(project=issue.project, user=user, is_owner=True)
vote = f.VoteFactory.create(content_object=issue, user=user)
url = reverse("issue-voters-detail", args=(issue.id, vote.user.id))
@ -75,6 +79,7 @@ def test_get_issue_voter(client):
def test_get_issue_votes(client):
user = f.UserFactory.create()
issue = f.create_issue(owner=user)
f.MembershipFactory.create(project=issue.project, user=user, is_owner=True)
url = reverse("issues-detail", args=(issue.id,))
f.VotesFactory.create(content_object=issue, count=5)