Files
SheetMe/tests/SheetMe.Core.Tests/PatientPreviewPolicyTests.cs
MsystechandClaude Opus 5 f50a7cff4b 환자 미리보기에서 부서 문을 뺀다 — 레거시에 없던 제약을 내가 붙였다
개발자 계정으로 환자 선택을 누르면 "전산실 소속만 가능합니다"가 떴다.
PatientPreviewPolicy 가 ModifyPolicy.CanModify 를 그대로 호출하고 있었기 때문이다.

## 근거를 다시 읽었다

레거시가 부서로 가리는 툴은 TK_MODIFY 하나뿐이다(frmSheetDesigner.vb:1030-1060).
TK_ExportXML / TK_ImportXML 은 MSYS 계정 문이고, TK_PREVIEW 에는
가시성 조건이 하나도 걸려 있지 않다 — 서식생성기를 열 수 있으면 쓸 수 있다.

즉 이 제약은 레거시에서 옮긴 것이 아니라 내가 "무게가 비슷하다"고 판단해
붙인 것이다. 사용자가 정한 것도 아니었다 — 환자 접근에 대한 결정은
"전체 검색 + 감사 로그"였고 부서 제한은 거기에 없다.

## 두 일의 무게가 실제로 다르다

수정은 지금 쓰이는 서식을 제자리에서 바꿔 병원 전체에 즉시 영향을 준다.
미리보기는 이미 그 사람이 볼 수 있는 기록을 읽기만 한다.

그리고 이 문은 막아서 더 위험해지는 쪽이었다. 서식을 만드는 사람이
자기 서식을 검증할 수 없으면 값이 틀린 서식이 그대로 운영에 올라간다.
태그가 정확한 값을 뽑는지 확인하는 것이 이 기능을 만든 이유였는데
그 확인을 만든 사람에게서 빼앗고 있었다.

## 바꾼 것

CanPreviewPatient 는 인증만 요구한다 — 미인증이면 감사 로그에 남길 주체가 없고
주체 없는 환자 조회는 남기면 안 되는 일이다. 그것이 유일한 조건이다.

병원·부서 인자는 받아 두고 쓰지 않는다. 병원별 규칙이 생기면 호출부를 고치지 않고
여기서 받을 수 있어야 한다.

안전장치는 막는 것이 아니라 감사 로그다. 그래서 테스트도 무게를 옮겼다 —
클래스 주석의 "감사와 권한이 유일한 장치"를 "감사 로그가 유일한 장치"로 고쳤고,
감사 한 줄의 모양(주체가 있는가·이름이 새지 않는가)이 가장 중요한 판정이 된다.

## 대조군

'수정_권한은_그대로_좁게_남는다' 를 새로 넣었다. 미리보기를 연 것이
수정까지 연 것이 되면 안 되고, 이 대조가 없으면 위 완화가 조용히
ModifyPolicy 까지 번져도 알 수 없다.

게이트: dotnet test 313/313 (미리보기 판정 1건 → 2건으로 갈라짐)

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 15:51:30 +09:00

72 lines
3.4 KiB
C#

using SheetMe.Core.Catalog;
namespace SheetMe.Core.Tests;
/// <summary>
/// 환자 미리보기 접근 규칙 고정.
///
/// 레거시는 인가코드가 소스에 평문이고, 검색 범위 무제한이고, 감사 0건이었다.
/// 검색 범위와 접근 범위는 레거시와 같게 두기로 했으므로 <b>감사 로그가 유일한 장치</b>다 —
/// 그래서 감사 한 줄의 모양(주체가 있는가, 이름이 새지 않는가)이 여기서 가장 중요한 판정이다.
/// </summary>
[TestClass]
public sealed class PatientPreviewPolicyTests
{
#region Methods
[TestMethod]
public void 미인증이면_볼_수_없다()
{
// 주체가 없으면 감사 로그에 남길 것이 없다 — 남길 수 없는 조회는 하지 않는다.
Assert.IsFalse(PatientPreviewPolicy.CanPreviewPatient("ABC", "EDPS", string.Empty, string.Empty));
StringAssert.Contains(
PatientPreviewPolicy.DenyReason("ABC", string.Empty, string.Empty), "HIS 사용자");
}
[TestMethod]
public void 부서로_막지_않는다()
{
// 처음에는 수정 권한(ModifyPolicy)을 그대로 갖다 썼다. 그건 레거시에 없는 제약이었다 —
// 레거시가 부서로 가리는 툴은 TK_MODIFY 하나뿐이고 TK_PREVIEW 에는 문이 없다
// (frmSheetDesigner.vb:1030-1060). 서식을 만드는 사람이 자기 서식을 검증할 수 없으면
// 값이 틀린 서식이 그대로 운영에 올라간다.
foreach (var dep in new[] { "EDPS", "NURS", "SALO", "MEDI", "" })
{
Assert.IsTrue(PatientPreviewPolicy.CanPreviewPatient("ABC", dep, "011825", string.Empty),
$"부서 '{dep}' 로 막혔다");
}
// 수정에만 걸리던 갈래도 미리보기에는 걸리지 않는다
Assert.IsTrue(PatientPreviewPolicy.CanPreviewPatient("XYZ", "NURS", "011825", "20250501"));
Assert.IsTrue(PatientPreviewPolicy.CanPreviewPatient("HANKOOK", "NURS", "011825", string.Empty));
}
[TestMethod]
public void 수정_권한은_그대로_좁게_남는다()
{
// 미리보기를 연 것이 수정까지 연 것이 되면 안 된다 — 수정은 지금 쓰이는 서식을
// 제자리에서 바꿔 병원 전체에 즉시 영향을 준다. 이 대조가 없으면 위 완화가
// 조용히 ModifyPolicy 까지 번져도 알 수 없다.
Assert.IsFalse(ModifyPolicy.CanModify("ABC", "NURS", "011825", string.Empty));
Assert.IsTrue(ModifyPolicy.CanModify("ABC", "EDPS", "011825", string.Empty));
}
[TestMethod]
public void 감사_한_줄에_주체와_내원이_들어간다()
{
var line = PatientPreviewPolicy.AuditLine("011825", "C12345");
StringAssert.Contains(line, "011825");
StringAssert.Contains(line, "C12345");
StringAssert.Contains(line, "환자 미리보기");
}
[TestMethod]
public void 감사_한_줄에_환자_이름은_넣지_않는다()
{
// 내원번호로 되짚을 수 있다. 로그 파일에 이름을 흘릴 이유가 없다 —
// 로그는 오래 남고 접근 통제가 DB 보다 느슨하다.
var line = PatientPreviewPolicy.AuditLine("011825", "C12345");
Assert.IsFalse(line.Contains("이름", StringComparison.Ordinal));
Assert.AreEqual(2, line.Split('=').Length - 1, $"항목이 둘(사용자·내원)이어야 한다: {line}");
}
#endregion
}