"""M02 층 권한 한 벌(PLAN 27-1c · `M02_Layer_Access`) — 나무 · 항목 · 양식 · 남의 층 목록 같은 규칙. ① `grant` 표 — 역할(일반 · 회사 관리자 · 시스템 관리자) × 층(마스터 · 자기 회사 · 남의 회사 · 자기 개인 · 같은 회사 남 · 다른 회사 남) = 읽기 · 수정 · 막힘. ② 임시 storage — 남의 층은 자리가 있을 때만(없으면 404 · 폴더 안 만듦) · 나무 · 양식 길 같은 답 · 남의 층 목록(회사 = 시스템 관리자 · 개인 = 회사 관리자 자기 회사 · 본인 뺌 · 자리 있는 것만). """ from __future__ import annotations from pathlib import Path from typing import Any import pytest from fastapi import HTTPException from config import config_system from M02_MasterTemplete import M02_Layer_Access as access from M02_MasterTemplete import M02_MasterTemplete_Router_Layers as layers_router from M02_MasterTemplete import M02_MasterTemplete_Store as store from M02_MasterTemplete import M02_Template_Layers as layers from resources.tester.test_m02_structure import write_sample_table from resources.tester.test_m02_tree import _app USER = {"user_id": 42, "company_id": 7, "role": "USER", "is_master": False} ADMIN = {**USER, "role": "ADMIN"} MASTER = {**USER, "is_master": True} SYSADMIN = {**USER, "role": "SYSTEM_ADMIN"} def _answer(session: dict[str, Any], layer: str, owner: str | None) -> str: try: return "수정" if access.grant(session, layer, owner).write else "읽기" except HTTPException as error: return str(error.status_code) def test_권한_표(): cases = [ ("system", None), ("company", None), ("company", "9"), ("personal", None), ("personal", "7/43"), ("personal", "9/90"), ] got = { name: [_answer(s, *c) for c in cases] for name, s in ( ("일반", USER), ("회사관리", ADMIN), ("마스터", MASTER), ("시스템", SYSADMIN), ) } assert got == { "일반": ["읽기", "읽기", "403", "수정", "403", "403"], "회사관리": ["읽기", "수정", "403", "수정", "읽기", "403"], "마스터": ["읽기", "수정", "403", "수정", "읽기", "403"], "시스템": ["수정", "수정", "읽기", "수정", "읽기", "읽기"], } # 자기 층 owner 를 적어도 같음 · 모양 틀림 400 · 회사 없음 409 assert access.grant(USER, "personal", "7/42").write is True assert _answer(SYSADMIN, "personal", "9") == "400" assert _answer({**USER, "company_id": None}, "company", None) == "409" with pytest.raises(HTTPException) as caught: access.require(ADMIN, "personal", "7/43", write=True) assert caught.value.status_code == 403 @pytest.fixture() def world(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> dict[str, Any]: system = tmp_path / "system" system.mkdir() write_sample_table(system) monkeypatch.setattr(store, "FOLDER", system) monkeypatch.setattr(layers, "SYSTEM_ROOT", system) store.renumber() storage = tmp_path / "storage" storage.mkdir() monkeypatch.setattr(config_system, "STORAGE_BASE_DIR", str(storage)) users = {42: 7, 43: 7, 44: 7, 90: 9} async def company_users(company_id: int) -> list[dict[str, Any]]: return [{"user_id": u, "name": f"u{u}"} for u, c in users.items() if c == company_id] async def companies() -> list[dict[str, Any]]: return [{"company_id": c, "name": f"c{c}"} for c in (7, 8, 9)] monkeypatch.setattr(layers_router, "_company_users", company_users) monkeypatch.setattr(layers_router, "_companies", companies) session: dict[str, Any] = dict(USER) return {"client": _app(session), "session": session, "storage": storage} def test_남의_층은_자리가_있을_때만_읽기(world: dict[str, Any]): client, session, storage = world["client"], world["session"], world["storage"] session.update(ADMIN) tree = "/api/m02/layers/personal/tree?owner=7/43" table = "/api/m02/layers/personal/templates/table/구조물집계표?owner=7/43" assert client.get(tree).status_code == 404 # 43 은 아직 아무것도 없음 assert not (storage / "7/43").exists() # 읽기로 남의 폴더를 만들지 않음 (storage / "7/43/templates").mkdir(parents=True) # 폴더만 있고 집계표 없음 assert client.get(tree).status_code == 404 assert client.get(table).status_code == 404 assert client.get("/api/m02/items?layer=personal&owner=7/43").status_code == 404 assert not any((storage / "7/43/templates").iterdir()) # 읽기로 남의 층에 뼈대를 안 씀 session.update(user_id=43) assert client.get("/api/m02/layers/personal/tree").status_code == 200 # 43 첫 진입 = 뼈대 session.update(user_id=42) assert client.get(tree).status_code == 200 assert client.get(table).status_code == 200 # 양식 길도 같은 규칙 body = {"op": "add-folder", "경로": [], "이름": "새", "판": client.get(tree).json()["판"]} assert client.post(tree, json=body).status_code == 403 # 읽기만 assert client.get("/api/m02/items?layer=personal&owner=7/43").status_code == 200 session.update(USER) assert client.get(tree).status_code == 403 assert client.get(table).status_code == 403 session.update(SYSADMIN) assert client.get(tree).status_code == 200 assert client.post(tree, json=body).status_code == 403 # 시스템 관리자도 남의 층은 읽기만 def test_남의_층_목록(world: dict[str, Any]): client, session, storage = world["client"], world["session"], world["storage"] for owner in ("7/43", "9/90"): # 층 폴더가 있는 사람만 목록에 (storage / owner / "templates").mkdir(parents=True) (storage / "9/templates").mkdir() people = "/api/m02/layers/personal/owners" assert client.get(people).status_code == 403 # 일반 사용자 assert client.get("/api/m02/layers/company/owners").status_code == 403 session.update(ADMIN) assert client.get(people).json() == [ {"owner": "7/43", "name": "u43"} ] # 본인 · 44(자리 없음) 뺌 assert client.get(f"{people}?company=9").status_code == 403 # 남의 회사 사람 session.update(SYSADMIN) assert client.get("/api/m02/layers/company/owners").json() == [ {"owner": "7", "name": "c7"}, # 자기 회사는 자리 없어도 {"owner": "9", "name": "c9"}, ] assert client.get(f"{people}?company=9").json() == [{"owner": "9/90", "name": "u90"}]