[WEB-5878] chore: add validation for project name/identifier for special characters (#8529)
* chore: update ProjectSerializer to raise validation for special characters in name and identifier * chore: update external endpoints * fix: external api serializer validation * update serializer to send error code * fix: move the regex expression to Project model
This commit is contained in:
parent
f0dcf66167
commit
c4b3d52466
3 changed files with 42 additions and 0 deletions
|
|
@ -6,6 +6,10 @@
|
||||||
import random
|
import random
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
|
|
||||||
|
# Python imports
|
||||||
|
import re
|
||||||
|
|
||||||
# Module imports
|
# Module imports
|
||||||
from plane.db.models import Project, ProjectIdentifier, WorkspaceMember, State, Estimate
|
from plane.db.models import Project, ProjectIdentifier, WorkspaceMember, State, Estimate
|
||||||
|
|
||||||
|
|
@ -101,6 +105,15 @@ class ProjectCreateSerializer(BaseSerializer):
|
||||||
]
|
]
|
||||||
|
|
||||||
def validate(self, data):
|
def validate(self, data):
|
||||||
|
project_name = data.get("name", None)
|
||||||
|
project_identifier = data.get("identifier", None)
|
||||||
|
|
||||||
|
if project_name is not None and re.match(Project.FORBIDDEN_IDENTIFIER_CHARS_PATTERN, project_name):
|
||||||
|
raise serializers.ValidationError("Project name cannot contain special characters.")
|
||||||
|
|
||||||
|
if project_identifier is not None and re.match(Project.FORBIDDEN_IDENTIFIER_CHARS_PATTERN, project_identifier):
|
||||||
|
raise serializers.ValidationError("Project identifier cannot contain special characters.")
|
||||||
|
|
||||||
if data.get("project_lead", None) is not None:
|
if data.get("project_lead", None) is not None:
|
||||||
# Check if the project lead is a member of the workspace
|
# Check if the project lead is a member of the workspace
|
||||||
if not WorkspaceMember.objects.filter(
|
if not WorkspaceMember.objects.filter(
|
||||||
|
|
@ -160,6 +173,15 @@ class ProjectUpdateSerializer(ProjectCreateSerializer):
|
||||||
read_only_fields = ProjectCreateSerializer.Meta.read_only_fields
|
read_only_fields = ProjectCreateSerializer.Meta.read_only_fields
|
||||||
|
|
||||||
def update(self, instance, validated_data):
|
def update(self, instance, validated_data):
|
||||||
|
project_name = validated_data.get("name", None)
|
||||||
|
project_identifier = validated_data.get("identifier", None)
|
||||||
|
|
||||||
|
if project_name is not None and re.match(Project.FORBIDDEN_IDENTIFIER_CHARS_PATTERN, project_name):
|
||||||
|
raise serializers.ValidationError("Project name cannot contain special characters.")
|
||||||
|
|
||||||
|
if project_identifier is not None and re.match(Project.FORBIDDEN_IDENTIFIER_CHARS_PATTERN, project_identifier):
|
||||||
|
raise serializers.ValidationError("Project identifier cannot contain special characters.")
|
||||||
|
|
||||||
"""Update a project"""
|
"""Update a project"""
|
||||||
if (
|
if (
|
||||||
validated_data.get("default_state", None) is not None
|
validated_data.get("default_state", None) is not None
|
||||||
|
|
@ -210,6 +232,15 @@ class ProjectSerializer(BaseSerializer):
|
||||||
]
|
]
|
||||||
|
|
||||||
def validate(self, data):
|
def validate(self, data):
|
||||||
|
project_name = data.get("name", None)
|
||||||
|
project_identifier = data.get("identifier", None)
|
||||||
|
|
||||||
|
if project_name is not None and re.match(Project.FORBIDDEN_IDENTIFIER_CHARS_PATTERN, project_name):
|
||||||
|
raise serializers.ValidationError("Project name cannot contain special characters.")
|
||||||
|
|
||||||
|
if project_identifier is not None and re.match(Project.FORBIDDEN_IDENTIFIER_CHARS_PATTERN, project_identifier):
|
||||||
|
raise serializers.ValidationError("Project identifier cannot contain special characters.")
|
||||||
|
|
||||||
# Check project lead should be a member of the workspace
|
# Check project lead should be a member of the workspace
|
||||||
if (
|
if (
|
||||||
data.get("project_lead", None) is not None
|
data.get("project_lead", None) is not None
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,9 @@
|
||||||
# Third party imports
|
# Third party imports
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
|
# Python imports
|
||||||
|
import re
|
||||||
|
|
||||||
# Module imports
|
# Module imports
|
||||||
from .base import BaseSerializer, DynamicBaseSerializer
|
from .base import BaseSerializer, DynamicBaseSerializer
|
||||||
from django.db.models import Max
|
from django.db.models import Max
|
||||||
|
|
@ -37,6 +40,9 @@ class ProjectSerializer(BaseSerializer):
|
||||||
project_id = self.instance.id if self.instance else None
|
project_id = self.instance.id if self.instance else None
|
||||||
workspace_id = self.context["workspace_id"]
|
workspace_id = self.context["workspace_id"]
|
||||||
|
|
||||||
|
if re.match(Project.FORBIDDEN_IDENTIFIER_CHARS_PATTERN, name):
|
||||||
|
raise serializers.ValidationError(detail="PROJECT_NAME_CANNOT_CONTAIN_SPECIAL_CHARACTERS")
|
||||||
|
|
||||||
project = Project.objects.filter(name=name, workspace_id=workspace_id)
|
project = Project.objects.filter(name=name, workspace_id=workspace_id)
|
||||||
|
|
||||||
if project_id:
|
if project_id:
|
||||||
|
|
@ -53,6 +59,9 @@ class ProjectSerializer(BaseSerializer):
|
||||||
project_id = self.instance.id if self.instance else None
|
project_id = self.instance.id if self.instance else None
|
||||||
workspace_id = self.context["workspace_id"]
|
workspace_id = self.context["workspace_id"]
|
||||||
|
|
||||||
|
if re.match(Project.FORBIDDEN_IDENTIFIER_CHARS_PATTERN, identifier):
|
||||||
|
raise serializers.ValidationError(detail="PROJECT_IDENTIFIER_CANNOT_CONTAIN_SPECIAL_CHARACTERS")
|
||||||
|
|
||||||
project = Project.objects.filter(identifier=identifier, workspace_id=workspace_id)
|
project = Project.objects.filter(identifier=identifier, workspace_id=workspace_id)
|
||||||
|
|
||||||
if project_id:
|
if project_id:
|
||||||
|
|
|
||||||
|
|
@ -140,6 +140,8 @@ class Project(BaseModel):
|
||||||
"""Return name of the project"""
|
"""Return name of the project"""
|
||||||
return f"{self.name} <{self.workspace.name}>"
|
return f"{self.name} <{self.workspace.name}>"
|
||||||
|
|
||||||
|
FORBIDDEN_IDENTIFIER_CHARS_PATTERN = r"^.*[&+,:;$^}{*=?@#|'<>.()%!-].*$"
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
unique_together = [
|
unique_together = [
|
||||||
["identifier", "workspace", "deleted_at"],
|
["identifier", "workspace", "deleted_at"],
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue