ФУНКЦИИ - МЕЖПРОЕКТНАЯ КОММУНИКАЦИЯ: Tasker workspace adapter API
This commit is contained in:
parent
8ec762f790
commit
a5a347e839
|
|
@ -0,0 +1,186 @@
|
|||
import json
|
||||
|
||||
from django.db import transaction
|
||||
from django.http import JsonResponse
|
||||
from django.utils.decorators import method_decorator
|
||||
from django.views import View
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
|
||||
from plane.authentication.views.nodedc_logout import is_internal_logout_request_authorized
|
||||
from plane.db.models import ExternalIdentityLink, Profile, User, Workspace, WorkspaceMember
|
||||
|
||||
|
||||
OIDC_PROVIDER = "authentik"
|
||||
ROLE_VALUES = {
|
||||
"guest": 5,
|
||||
"viewer": 5,
|
||||
"member": 15,
|
||||
"admin": 20,
|
||||
"owner": 20,
|
||||
5: 5,
|
||||
15: 15,
|
||||
20: 20,
|
||||
}
|
||||
|
||||
|
||||
def internal_unauthorized_response():
|
||||
return JsonResponse({"ok": False, "error": "internal_access_unauthorized"}, status=401)
|
||||
|
||||
|
||||
def parse_json_body(request):
|
||||
if not request.body:
|
||||
return {}
|
||||
|
||||
try:
|
||||
return json.loads(request.body.decode("utf-8"))
|
||||
except (UnicodeDecodeError, json.JSONDecodeError):
|
||||
return None
|
||||
|
||||
|
||||
def normalize_email(value):
|
||||
return value.strip().lower() if isinstance(value, str) else ""
|
||||
|
||||
|
||||
def resolve_workspace(payload):
|
||||
workspace_id = payload.get("workspaceId") or payload.get("workspace_id")
|
||||
workspace_slug = payload.get("workspaceSlug") or payload.get("workspace_slug") or payload.get("slug")
|
||||
|
||||
queryset = Workspace.objects.filter(deleted_at__isnull=True)
|
||||
if workspace_id:
|
||||
return queryset.filter(id=workspace_id).first()
|
||||
if workspace_slug:
|
||||
return queryset.filter(slug=workspace_slug).first()
|
||||
return None
|
||||
|
||||
|
||||
def resolve_user(payload):
|
||||
plane_user_id = payload.get("planeUserId") or payload.get("plane_user_id")
|
||||
subject = payload.get("subject")
|
||||
email = normalize_email(payload.get("email"))
|
||||
|
||||
if plane_user_id:
|
||||
user = User.objects.filter(id=plane_user_id, is_bot=False).first()
|
||||
if user:
|
||||
return user
|
||||
|
||||
if subject:
|
||||
link = ExternalIdentityLink.objects.filter(
|
||||
provider=OIDC_PROVIDER,
|
||||
subject=subject,
|
||||
status=ExternalIdentityLink.Status.ACTIVE,
|
||||
).select_related("user").first()
|
||||
if link:
|
||||
return link.user
|
||||
|
||||
if email:
|
||||
link = ExternalIdentityLink.objects.filter(
|
||||
provider=OIDC_PROVIDER,
|
||||
email__iexact=email,
|
||||
status=ExternalIdentityLink.Status.ACTIVE,
|
||||
).select_related("user").first()
|
||||
if link:
|
||||
return link.user
|
||||
return User.objects.filter(email__iexact=email, is_bot=False).first()
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def normalize_role(value):
|
||||
return ROLE_VALUES.get(value, 15)
|
||||
|
||||
|
||||
def serialize_workspace(workspace):
|
||||
return {
|
||||
"id": str(workspace.id),
|
||||
"slug": workspace.slug,
|
||||
"name": workspace.name,
|
||||
"ownerEmail": workspace.owner.email if workspace.owner_id else None,
|
||||
"memberCount": WorkspaceMember.objects.filter(
|
||||
workspace=workspace,
|
||||
deleted_at__isnull=True,
|
||||
is_active=True,
|
||||
member__is_bot=False,
|
||||
).count(),
|
||||
}
|
||||
|
||||
|
||||
def serialize_membership(membership, created):
|
||||
return {
|
||||
"created": created,
|
||||
"workspace": serialize_workspace(membership.workspace),
|
||||
"member": {
|
||||
"id": str(membership.member.id),
|
||||
"email": membership.member.email,
|
||||
"displayName": membership.member.display_name,
|
||||
},
|
||||
"role": membership.role,
|
||||
"isActive": membership.is_active,
|
||||
"isBanned": membership.is_banned,
|
||||
}
|
||||
|
||||
|
||||
@method_decorator(csrf_exempt, name="dispatch")
|
||||
class NodeDCInternalWorkspaceListEndpoint(View):
|
||||
def get(self, request):
|
||||
if not is_internal_logout_request_authorized(request):
|
||||
return internal_unauthorized_response()
|
||||
|
||||
workspaces = Workspace.objects.filter(deleted_at__isnull=True).select_related("owner").order_by("name")
|
||||
return JsonResponse({"ok": True, "workspaces": [serialize_workspace(workspace) for workspace in workspaces]})
|
||||
|
||||
|
||||
@method_decorator(csrf_exempt, name="dispatch")
|
||||
class NodeDCInternalWorkspaceMembershipEnsureEndpoint(View):
|
||||
def post(self, request):
|
||||
if not is_internal_logout_request_authorized(request):
|
||||
return internal_unauthorized_response()
|
||||
|
||||
payload = parse_json_body(request)
|
||||
if payload is None:
|
||||
return JsonResponse({"ok": False, "error": "invalid_json"}, status=400)
|
||||
|
||||
workspace = resolve_workspace(payload)
|
||||
if workspace is None:
|
||||
return JsonResponse({"ok": False, "error": "workspace_not_found"}, status=404)
|
||||
|
||||
user = resolve_user(payload)
|
||||
if user is None:
|
||||
return JsonResponse({"ok": False, "error": "user_not_found"}, status=404)
|
||||
|
||||
role = normalize_role(payload.get("role"))
|
||||
company_role = payload.get("companyRole") or payload.get("company_role")
|
||||
set_last_workspace = payload.get("setLastWorkspace", True) is not False
|
||||
|
||||
with transaction.atomic():
|
||||
membership = WorkspaceMember.objects.filter(
|
||||
workspace=workspace,
|
||||
member=user,
|
||||
deleted_at__isnull=True,
|
||||
).first()
|
||||
created = membership is None
|
||||
|
||||
if membership is None:
|
||||
membership = WorkspaceMember.objects.create(
|
||||
workspace=workspace,
|
||||
member=user,
|
||||
role=role,
|
||||
company_role=company_role if isinstance(company_role, str) else None,
|
||||
is_active=True,
|
||||
is_banned=False,
|
||||
)
|
||||
else:
|
||||
membership.role = role
|
||||
if isinstance(company_role, str):
|
||||
membership.company_role = company_role
|
||||
membership.is_active = True
|
||||
membership.is_banned = False
|
||||
membership.banned_at = None
|
||||
membership.banned_until = None
|
||||
membership.save(update_fields=["role", "company_role", "is_active", "is_banned", "banned_at", "banned_until", "updated_at"])
|
||||
|
||||
if set_last_workspace:
|
||||
profile, _ = Profile.objects.get_or_create(user=user)
|
||||
profile.last_workspace_id = workspace.id
|
||||
profile.save(update_fields=["last_workspace_id", "updated_at"])
|
||||
|
||||
return JsonResponse({"ok": True, "membership": serialize_membership(membership, created)})
|
||||
|
|
@ -15,6 +15,10 @@ from plane.authentication.views.nodedc_logout import (
|
|||
NodeDCFrontChannelLogoutEndpoint,
|
||||
NodeDCInternalSessionLogoutEndpoint,
|
||||
)
|
||||
from plane.authentication.views.nodedc_workspace_adapter import (
|
||||
NodeDCInternalWorkspaceListEndpoint,
|
||||
NodeDCInternalWorkspaceMembershipEnsureEndpoint,
|
||||
)
|
||||
|
||||
handler404 = "plane.app.views.error_404.custom_404_view"
|
||||
|
||||
|
|
@ -24,6 +28,16 @@ urlpatterns = [
|
|||
NodeDCInternalSessionLogoutEndpoint.as_view(),
|
||||
name="nodedc-internal-session-logout",
|
||||
),
|
||||
path(
|
||||
"api/internal/nodedc/workspaces/",
|
||||
NodeDCInternalWorkspaceListEndpoint.as_view(),
|
||||
name="nodedc-internal-workspaces",
|
||||
),
|
||||
path(
|
||||
"api/internal/nodedc/workspace-memberships/ensure/",
|
||||
NodeDCInternalWorkspaceMembershipEnsureEndpoint.as_view(),
|
||||
name="nodedc-internal-workspace-membership-ensure",
|
||||
),
|
||||
path("api/", include("plane.app.urls")),
|
||||
path("api/public/", include("plane.space.urls")),
|
||||
path("api/instances/", include("plane.license.urls")),
|
||||
|
|
|
|||
Loading…
Reference in New Issue