forked from github/plane
Merge branch 'chore/api_endpoints' of github.com:makeplane/plane into chore/api_endpoints
This commit is contained in:
commit
2231487b18
@ -1,3 +1,9 @@
|
|||||||
|
# Python imports
|
||||||
|
import urllib
|
||||||
|
import socket
|
||||||
|
import ipaddress
|
||||||
|
from urllib.parse import urlparse
|
||||||
|
|
||||||
# Third party imports
|
# Third party imports
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
@ -9,6 +15,42 @@ from plane.db.models.webhook import validate_domain, validate_schema
|
|||||||
class WebhookSerializer(DynamicBaseSerializer):
|
class WebhookSerializer(DynamicBaseSerializer):
|
||||||
url = serializers.URLField(validators=[validate_schema, validate_domain])
|
url = serializers.URLField(validators=[validate_schema, validate_domain])
|
||||||
|
|
||||||
|
def validate(self, data):
|
||||||
|
url = data.get("url", None)
|
||||||
|
|
||||||
|
# Extract the hostname from the URL
|
||||||
|
hostname = urlparse(url).hostname
|
||||||
|
if not hostname:
|
||||||
|
raise serializers.ValidationError({"url": "Invalid URL: No hostname found."})
|
||||||
|
|
||||||
|
# Resolve the hostname to IP addresses
|
||||||
|
try:
|
||||||
|
ip_addresses = socket.getaddrinfo(hostname, None)
|
||||||
|
except socket.gaierror:
|
||||||
|
raise serializers.ValidationError({"url": "Hostname could not be resolved."})
|
||||||
|
|
||||||
|
if not ip_addresses:
|
||||||
|
raise serializers.ValidationError({"url": "No IP addresses found for the hostname."})
|
||||||
|
|
||||||
|
for addr in ip_addresses:
|
||||||
|
ip = ipaddress.ip_address(addr[4][0])
|
||||||
|
if ip.is_private or ip.is_loopback:
|
||||||
|
raise serializers.ValidationError({"url": "URL resolves to a blocked IP address."})
|
||||||
|
|
||||||
|
# Additional validation for multiple request domains and their subdomains
|
||||||
|
request = self.context.get('request')
|
||||||
|
disallowed_domains = ['plane.so',] # Add your disallowed domains here
|
||||||
|
if request:
|
||||||
|
request_host = request.get_host().split(':')[0] # Remove port if present
|
||||||
|
disallowed_domains.append(request_host)
|
||||||
|
|
||||||
|
# Check if hostname is a subdomain or exact match of any disallowed domain
|
||||||
|
if any(hostname == domain or hostname.endswith('.' + domain) for domain in disallowed_domains):
|
||||||
|
raise serializers.ValidationError({"url": "URL domain or its subdomain is not allowed."})
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Webhook
|
model = Webhook
|
||||||
fields = "__all__"
|
fields = "__all__"
|
||||||
|
@ -65,6 +65,7 @@ class WebhookMixin:
|
|||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class BaseViewSet(TimezoneMixin, ModelViewSet, BasePaginator):
|
class BaseViewSet(TimezoneMixin, ModelViewSet, BasePaginator):
|
||||||
model = None
|
model = None
|
||||||
|
|
||||||
|
@ -48,7 +48,7 @@ class UserEndpoint(BaseViewSet):
|
|||||||
if WorkspaceMember.objects.filter(member=request.user, is_active=True).exists():
|
if WorkspaceMember.objects.filter(member=request.user, is_active=True).exists():
|
||||||
return Response(
|
return Response(
|
||||||
{
|
{
|
||||||
"error": "User cannot deactivate account as user is active in some workspaces"
|
"error": "You cannot deactivate account as you are a member in some workspaces."
|
||||||
},
|
},
|
||||||
status=status.HTTP_400_BAD_REQUEST,
|
status=status.HTTP_400_BAD_REQUEST,
|
||||||
)
|
)
|
||||||
|
@ -20,9 +20,10 @@ class WebhookEndpoint(BaseAPIView):
|
|||||||
|
|
||||||
def post(self, request, slug):
|
def post(self, request, slug):
|
||||||
workspace = Workspace.objects.get(slug=slug)
|
workspace = Workspace.objects.get(slug=slug)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
serializer = WebhookSerializer(data=request.data)
|
serializer = WebhookSerializer(
|
||||||
|
data=request.data, context={"request": request}
|
||||||
|
)
|
||||||
if serializer.is_valid():
|
if serializer.is_valid():
|
||||||
serializer.save(workspace_id=workspace.id)
|
serializer.save(workspace_id=workspace.id)
|
||||||
return Response(serializer.data, status=status.HTTP_201_CREATED)
|
return Response(serializer.data, status=status.HTTP_201_CREATED)
|
||||||
@ -79,6 +80,7 @@ class WebhookEndpoint(BaseAPIView):
|
|||||||
serializer = WebhookSerializer(
|
serializer = WebhookSerializer(
|
||||||
webhook,
|
webhook,
|
||||||
data=request.data,
|
data=request.data,
|
||||||
|
context={request: request},
|
||||||
partial=True,
|
partial=True,
|
||||||
fields=(
|
fields=(
|
||||||
"id",
|
"id",
|
||||||
|
@ -600,7 +600,7 @@ class WorkSpaceMemberViewSet(BaseViewSet):
|
|||||||
):
|
):
|
||||||
return Response(
|
return Response(
|
||||||
{
|
{
|
||||||
"error": "User is part of some projects where they are the only admin you should leave that project first"
|
"error": "User is a part of some projects where they are the only admin, they should either leave that project or promote another user to admin."
|
||||||
},
|
},
|
||||||
status=status.HTTP_400_BAD_REQUEST,
|
status=status.HTTP_400_BAD_REQUEST,
|
||||||
)
|
)
|
||||||
@ -635,7 +635,7 @@ class WorkSpaceMemberViewSet(BaseViewSet):
|
|||||||
):
|
):
|
||||||
return Response(
|
return Response(
|
||||||
{
|
{
|
||||||
"error": "You cannot leave the workspace as your the only admin of the workspace you will have to either delete the workspace or create an another admin"
|
"error": "You cannot leave the workspace as you are the only admin of the workspace you will have to either delete the workspace or promote another user to admin."
|
||||||
},
|
},
|
||||||
status=status.HTTP_400_BAD_REQUEST,
|
status=status.HTTP_400_BAD_REQUEST,
|
||||||
)
|
)
|
||||||
@ -656,7 +656,7 @@ class WorkSpaceMemberViewSet(BaseViewSet):
|
|||||||
):
|
):
|
||||||
return Response(
|
return Response(
|
||||||
{
|
{
|
||||||
"error": "User is part of some projects where they are the only admin you should leave that project first"
|
"error": "You are a part of some projects where you are the only admin, you should either leave the project or promote another user to admin."
|
||||||
},
|
},
|
||||||
status=status.HTTP_400_BAD_REQUEST,
|
status=status.HTTP_400_BAD_REQUEST,
|
||||||
)
|
)
|
||||||
|
@ -16,7 +16,6 @@ def generate_token():
|
|||||||
|
|
||||||
def validate_schema(value):
|
def validate_schema(value):
|
||||||
parsed_url = urlparse(value)
|
parsed_url = urlparse(value)
|
||||||
print(parsed_url)
|
|
||||||
if parsed_url.scheme not in ["http", "https"]:
|
if parsed_url.scheme not in ["http", "https"]:
|
||||||
raise ValidationError("Invalid schema. Only HTTP and HTTPS are allowed.")
|
raise ValidationError("Invalid schema. Only HTTP and HTTPS are allowed.")
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user