ARCH - TASKER BIM: серверный CAD-шлюз и независимый просмотр из Ops
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
# Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
# See the LICENSE file for details.
|
||||
|
||||
import base64
|
||||
import json
|
||||
from types import SimpleNamespace
|
||||
from urllib.parse import parse_qs, urlsplit
|
||||
|
||||
import pytest
|
||||
|
||||
from plane.utils.nodedc_bim_gateway import (
|
||||
BimGatewayError,
|
||||
build_bim_embed_url,
|
||||
get_bim_direct_model_type,
|
||||
get_bim_model_type,
|
||||
resolve_bim_viewer_model,
|
||||
to_bim_relative_asset_url,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_cad_extensions_are_normalized():
|
||||
assert get_bim_model_type("part.STEP") == "step"
|
||||
assert get_bim_model_type("part.stp") == "step"
|
||||
assert get_bim_model_type("part.IGES") == "iges"
|
||||
assert get_bim_model_type("part.igs") == "iges"
|
||||
assert get_bim_direct_model_type("part.glb") == "gltf"
|
||||
assert get_bim_direct_model_type("part.step") is None
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_only_managed_public_bim_sources_are_accepted(monkeypatch):
|
||||
monkeypatch.setenv("PLANE_NODEDC_BIM_PUBLIC_URL", "https://bim.example")
|
||||
|
||||
assert (
|
||||
to_bim_relative_asset_url("https://bim.example/uploads/project/model.glb?version=2")
|
||||
== "/uploads/project/model.glb?version=2"
|
||||
)
|
||||
assert to_bim_relative_asset_url("/data/project/model.xkt") == "/data/project/model.xkt"
|
||||
assert to_bim_relative_asset_url("https://attacker.example/uploads/model.glb") is None
|
||||
assert to_bim_relative_asset_url("/api/auth/session") is None
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_embed_grant_is_short_lived_and_bound_to_ops_context(monkeypatch):
|
||||
monkeypatch.setenv("PLANE_NODEDC_BIM_PUBLIC_URL", "https://bim.example")
|
||||
monkeypatch.setenv("PLANE_NODEDC_BIM_EMBED_SECRET", "test-embed-secret")
|
||||
monkeypatch.setenv("PLANE_NODEDC_BIM_EMBED_TTL_SECONDS", "180")
|
||||
model = {
|
||||
"url": "/uploads/project/model.glb",
|
||||
"settingsSrc": "/uploads/project/model.glb",
|
||||
"type": "gltf",
|
||||
"name": "model.glb",
|
||||
}
|
||||
|
||||
viewer_url, expires_at = build_bim_embed_url(
|
||||
model,
|
||||
SimpleNamespace(id="user-1"),
|
||||
"workspace",
|
||||
"project-1",
|
||||
"issue-1",
|
||||
"attachment-1",
|
||||
)
|
||||
|
||||
query = parse_qs(urlsplit(viewer_url).query)
|
||||
encoded_payload = query["token"][0].split(".", 1)[0]
|
||||
encoded_payload += "=" * (-len(encoded_payload) % 4)
|
||||
claims = json.loads(base64.urlsafe_b64decode(encoded_payload).decode("utf-8"))
|
||||
assert urlsplit(viewer_url).path == "/embed/tasker"
|
||||
assert claims["sub"] == "user-1"
|
||||
assert claims["workspace"] == "workspace"
|
||||
assert claims["project"] == "project-1"
|
||||
assert claims["issue"] == "issue-1"
|
||||
assert claims["attachment"] == "attachment-1"
|
||||
assert claims["model"] == model
|
||||
assert expires_at - claims["iat"] == 180
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_viewer_model_rejects_unmanaged_or_unready_sources(monkeypatch):
|
||||
monkeypatch.setenv("PLANE_NODEDC_BIM_PUBLIC_URL", "https://bim.example")
|
||||
|
||||
with pytest.raises(BimGatewayError, match="still being prepared"):
|
||||
resolve_bim_viewer_model(
|
||||
{
|
||||
"originalFilename": "part.step",
|
||||
"previewAvailable": False,
|
||||
"src": "https://bim.example/uploads/project/part.step",
|
||||
"type": "step",
|
||||
}
|
||||
)
|
||||
|
||||
with pytest.raises(BimGatewayError, match="outside the managed BIM storage"):
|
||||
resolve_bim_viewer_model(
|
||||
{
|
||||
"originalFilename": "model.glb",
|
||||
"previewAvailable": True,
|
||||
"src": "https://attacker.example/uploads/model.glb",
|
||||
"type": "gltf",
|
||||
}
|
||||
)
|
||||
Reference in New Issue
Block a user