Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 81 additions & 0 deletions connect/common/migrations/0094_auto_20251203_1800.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# Generated by Django 3.2.23 on 2025-12-03 18:00

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("common", "0093_project_language"),
]

operations = [
migrations.AlterField(
model_name="organizationauthorization",
name="role",
field=models.PositiveIntegerField(
choices=[
(0, "not set"),
(2, "contributor"),
(3, "admin"),
(1, "viewer"),
(4, "financial"),
(5, "support"),
(6, "marketing"),
],
default=0,
verbose_name="role",
),
),
migrations.AlterField(
model_name="projectauthorization",
name="role",
field=models.PositiveIntegerField(
choices=[
(0, "not set"),
(1, "viewer"),
(2, "contributor"),
(3, "moderator"),
(4, "support"),
(5, "Chat user"),
(6, "marketing"),
],
default=0,
verbose_name="role",
),
),
migrations.AlterField(
model_name="requestpermissionorganization",
name="role",
field=models.PositiveIntegerField(
choices=[
(0, "not set"),
(2, "contributor"),
(3, "admin"),
(1, "viewer"),
(4, "financial"),
(5, "support"),
(6, "marketing"),
],
default=0,
verbose_name="role",
),
),
migrations.AlterField(
model_name="requestpermissionproject",
name="role",
field=models.PositiveIntegerField(
choices=[
(0, "not set"),
(1, "viewer"),
(2, "contributor"),
(3, "moderator"),
(4, "support"),
(5, "Chat user"),
(6, "marketing"),
],
default=0,
verbose_name="role",
),
),
]
23 changes: 19 additions & 4 deletions connect/common/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -298,11 +298,11 @@ def create_ai_organization(self, user_email: str):


class OrganizationLevelRole(Enum):
NOTHING, VIEWER, CONTRIBUTOR, ADMIN, FINANCIAL, SUPPORT = list(range(6))
NOTHING, VIEWER, CONTRIBUTOR, ADMIN, FINANCIAL, SUPPORT, MARKETING = list(range(7))


class OrganizationRole(Enum):
NOT_SETTED, VIEWER, CONTRIBUTOR, ADMIN, FINANCIAL, SUPPORT = list(range(6))
NOT_SETTED, VIEWER, CONTRIBUTOR, ADMIN, FINANCIAL, SUPPORT, MARKETING = list(range(7))


class OrganizationAuthorization(models.Model):
Expand All @@ -318,6 +318,7 @@ class Meta:
(OrganizationRole.VIEWER.value, _("viewer")),
(OrganizationRole.FINANCIAL.value, _("financial")),
(OrganizationRole.SUPPORT.value, _("support")),
(OrganizationRole.MARKETING.value, _("marketing")),
]

uuid = models.UUIDField(
Expand Down Expand Up @@ -356,6 +357,9 @@ def level(self):
if self.role == OrganizationRole.SUPPORT.value:
return OrganizationLevelRole.SUPPORT.value

if self.role == OrganizationRole.MARKETING.value:
return OrganizationLevelRole.MARKETING.value

@property
def can_read(self):
return self.level in [
Expand All @@ -364,6 +368,7 @@ def can_read(self):
OrganizationLevelRole.ADMIN.value,
OrganizationLevelRole.VIEWER.value,
OrganizationLevelRole.SUPPORT.value,
OrganizationLevelRole.MARKETING.value,
]

@property
Expand All @@ -372,13 +377,15 @@ def can_contribute(self):
OrganizationLevelRole.CONTRIBUTOR.value,
OrganizationLevelRole.ADMIN.value,
OrganizationLevelRole.SUPPORT.value,
OrganizationLevelRole.MARKETING.value,
]

@property
def can_write(self):
return self.level in [
OrganizationLevelRole.ADMIN.value,
OrganizationLevelRole.SUPPORT.value,
OrganizationLevelRole.MARKETING.value,
]

@property
Expand All @@ -398,6 +405,7 @@ def can_contribute_billing(self):
OrganizationLevelRole.ADMIN.value,
OrganizationLevelRole.FINANCIAL.value,
OrganizationLevelRole.SUPPORT.value,
OrganizationLevelRole.MARKETING.value,
]

@property
Expand Down Expand Up @@ -879,11 +887,11 @@ def level(self):


class ProjectRole(Enum):
NOT_SETTED, VIEWER, CONTRIBUTOR, MODERATOR, SUPPORT, CHAT_USER = list(range(6))
NOT_SETTED, VIEWER, CONTRIBUTOR, MODERATOR, SUPPORT, CHAT_USER, MARKETING = list(range(7))


class ProjectRoleLevel(Enum):
NOTHING, VIEWER, CONTRIBUTOR, MODERATOR, SUPPORT, CHAT_USER = list(range(6))
NOTHING, VIEWER, CONTRIBUTOR, MODERATOR, SUPPORT, CHAT_USER, MARKETING = list(range(7))


class ProjectAuthorization(models.Model):
Expand All @@ -897,6 +905,7 @@ class Meta:
(ProjectRole.MODERATOR.value, _("moderator")),
(ProjectRole.SUPPORT.value, _("support")),
(ProjectRole.CHAT_USER.value, _("Chat user")),
(ProjectRole.MARKETING.value, _("marketing")),
]
uuid = models.UUIDField(
_("UUID"), primary_key=True, default=uuid4.uuid4, editable=False
Expand Down Expand Up @@ -934,19 +943,23 @@ def level(self):
return ProjectRoleLevel.VIEWER.value
elif self.role == ProjectRole.SUPPORT.value:
return ProjectRoleLevel.SUPPORT.value
elif self.role == ProjectRole.MARKETING.value:
return ProjectRoleLevel.MARKETING.value

@property
def is_moderator(self):
return self.level in [
ProjectRoleLevel.MODERATOR.value,
ProjectRoleLevel.SUPPORT.value,
ProjectRoleLevel.MARKETING.value,
]

@property
def can_write(self):
return self.level in [
ProjectRoleLevel.MODERATOR.value,
ProjectRoleLevel.SUPPORT.value,
ProjectRoleLevel.MARKETING.value,
]

@property
Expand All @@ -957,6 +970,7 @@ def can_read(self):
ProjectRoleLevel.VIEWER.value,
ProjectRoleLevel.SUPPORT.value,
ProjectRoleLevel.CHAT_USER.value,
ProjectRoleLevel.MARKETING.value,
]

@property
Expand All @@ -965,6 +979,7 @@ def can_contribute(self):
ProjectRoleLevel.MODERATOR.value,
ProjectRoleLevel.CONTRIBUTOR.value,
ProjectRoleLevel.SUPPORT.value,
ProjectRoleLevel.MARKETING.value,
]


Expand Down
1 change: 0 additions & 1 deletion connect/sentry/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ def ready(self) -> None:
if not settings.USE_SENTRY:
return

if settings.USE_SENTRY:
sentry_sdk.init(
dsn=settings.SENTRY_URL,
integrations=[DjangoIntegration()],
Expand Down
6 changes: 3 additions & 3 deletions connect/usecases/authorizations/create.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def create_organization_authorization(
"has_2fa": user.has_2fa,
},
)

if not created:
authorization.role = auth_dto.role
authorization.has_2fa = user.has_2fa
Expand Down Expand Up @@ -62,7 +62,7 @@ def create_project_authorization(
"role": role,
},
)

if not created:
project_auth.role = role
project_auth.save()
Expand Down Expand Up @@ -183,6 +183,6 @@ def create_request_permission_for_user_that_dosent_exist(
request_permission.role = auth_dto.role
request_permission.created_by = created_by_user
request_permission.save()

project.send_email_invite_project(request_permission.email)
return request_permission
106 changes: 106 additions & 0 deletions connect/usecases/authorizations/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
RequestPermissionProject,
OrganizationAuthorization,
ProjectAuthorization,
OrganizationRole,
ProjectRole,
)

from connect.usecases.authorizations.create import CreateAuthorizationUseCase
Expand Down Expand Up @@ -423,3 +425,107 @@ def test_create_org(self):

self.request_permission_project(data)
self.assertEquals(ProjectAuthorization.objects.count(), 2)


class MarketingRoleTestCase(TestCase, TestCaseSetUp):
"""Tests for the MARKETING role (role=6) at organization and project levels."""

def setUp(self):
self.superuser = User.objects.create(
email="super@test.user", username="superuser"
)
self.user = User.objects.create(
email="marketing@test.user", username="MarketingTestUser", has_2fa=True
)
self.org = Organization.objects.create(
name="Marketing test org",
description="Marketing test org",
inteligence_organization=1,
organization_billing__cycle=BillingPlan.BILLING_CYCLE_MONTHLY,
organization_billing__plan=BillingPlan.PLAN_TRIAL,
)
self.project = self.org.project.create(name="Marketing test project")

def test_marketing_role_value(self):
"""Test that MARKETING role has correct enum value."""
self.assertEqual(OrganizationRole.MARKETING.value, 6)
self.assertEqual(ProjectRole.MARKETING.value, 6)

def test_marketing_org_authorization_properties(self):
"""Test that MARKETING role has correct permission properties at org level."""
role = OrganizationRole.MARKETING.value
self.create_auth(self.user, self.org, role)
authorization = self.org.authorizations.get(user=self.user)

self.assertEqual(authorization.role, 6)
self.assertTrue(authorization.can_read)
self.assertTrue(authorization.can_contribute)
self.assertTrue(authorization.can_write)
self.assertFalse(authorization.is_admin)

def test_marketing_role_maps_to_project(self):
"""Test that MARKETING org role propagates correctly to project."""
role = OrganizationRole.MARKETING.value
self.create_auth(self.user, self.org, role, publish_message=False)

project_auth = self.project.get_user_authorization(self.user)
self.assertEqual(project_auth.role, ProjectRole.MARKETING.value)

def test_marketing_project_authorization_properties(self):
"""Test that MARKETING role has correct permission properties at project level."""
role = OrganizationRole.MARKETING.value
self.create_auth(self.user, self.org, role, publish_message=False)

project_auth = self.project.get_user_authorization(self.user)

self.assertEqual(project_auth.role, 6)
self.assertTrue(project_auth.can_read)
self.assertTrue(project_auth.can_contribute)
self.assertTrue(project_auth.can_write)
self.assertTrue(project_auth.is_moderator)
self.assertFalse(project_auth.is_admin)

def test_update_to_marketing_role(self):
"""Test updating an existing authorization to MARKETING role."""
# First create with CONTRIBUTOR role
initial_role = OrganizationRole.CONTRIBUTOR.value
self.create_auth(self.user, self.org, initial_role)

# Update to MARKETING role
update_role = OrganizationRole.MARKETING.value
auth_dto = UpdateAuthorizationDTO(
user_email=self.user.email,
org_uuid=str(self.org.uuid),
role=update_role,
request_user=self.superuser.email,
)

usecase = UpdateAuthorizationUseCase(message_publisher=MockRabbitMQPublisher())
authorization = usecase.update_authorization(auth_dto)

project_auth = self.project.get_user_authorization(self.user)

self.assertEqual(authorization.role, update_role)
self.assertEqual(project_auth.role, ProjectRole.MARKETING.value)

def test_create_marketing_authorization_for_single_project(self):
"""Test creating MARKETING authorization for a single project."""
# First create superuser auth to have permission
self.org.authorizations.create(user=self.superuser, role=OrganizationRole.ADMIN.value)
ProjectAuthorization.objects.create(
user=self.superuser,
project=self.project,
role=ProjectRole.MODERATOR.value,
)

role = ProjectRole.MARKETING.value
auth_dto = CreateProjectAuthorizationDTO(
user_email=self.user.email,
project_uuid=str(self.project.uuid),
role=role,
created_by_email=self.superuser.email,
)
usecase = CreateAuthorizationUseCase(MockRabbitMQPublisher())
project_auth = usecase.create_authorization_for_a_single_project(auth_dto)

self.assertEqual(project_auth.role, role)
1 change: 1 addition & 0 deletions connect/usecases/authorizations/usecase.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ class AuthorizationUseCase:
OrganizationRole.ADMIN.value: ProjectRole.MODERATOR.value,
OrganizationRole.CONTRIBUTOR.value: ProjectRole.CONTRIBUTOR.value,
OrganizationRole.SUPPORT.value: ProjectRole.SUPPORT.value,
OrganizationRole.MARKETING.value: ProjectRole.MARKETING.value,
}

def __init__(
Expand Down
2 changes: 1 addition & 1 deletion connect/usecases/organizations/eda_publisher.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Dict, Optional
from typing import Optional
from uuid import UUID

import pendulum
Expand Down
Loading