토글을 숨겨도 요청 본문은 만들 수 있다
학교 운영자 화면에서 LMS·CMS 연동 권한을 숨겨도, 그 필드를 담은 HTTP 요청은 직접 만들 수 있다. 같은 수정 DTO가 모든 권한을 받는다면 화면의 구분이 서버의 쓰기 권한으로 이어지지 않는다.
AI Commons에서는 학교 운영자가 수정하는 네 개의 기능 권한과 자이닉스 운영자가 수정하는 세 개의 연동 권한을 서로 다른 DTO로 나눴다. 학교용 DTO에서 연동 필드를 제거하고, 연동용 세 엔드포인트에는 클래스의 운영자 인증에 더해 @XinicsAdmin()을 붙였다. 당시 전역 ValidationPipe의 whitelist: true와 forbidNonWhitelisted: true는 허용되지 않은 필드를 받으면 거부하도록 설정돼 있었다.
이 글은 라이선스 체계로 전환하기 전의 Permission 모듈을 다룬다. 이후 인가를 라이선스 체계로 옮기면서 해당 모듈과 대응 테스트가 제거됐다. 아래의 DTO와 JSON 갱신 경로는 전환 전 구현의 경계다.
작아진 DTO로 전체 JSON을 교체하면 기존 권한이 사라진다
요청 필드를 줄이는 것만으로는 끝나지 않는다. 한 JSON 행에 두 운영자의 권한을 함께 저장하므로, 학교용 DTO를 전체 권한으로 해석해 저장하면 요청에 없는 LMS·CMS 값까지 덮어쓸 수 있다. 변경된 서비스는 기존 값을 읽고 허용한 네 필드만 덮은 뒤 종속 규칙을 적용한다. 다음은 역할 권한 갱신의 병합 부분이다.
const merged: PermissionData = {
...currentData,
canAccessAiCommons: dto.canAccessAiCommons,
canAccessSlides: dto.canAccessSlides,
canAccessVideoGeneration: dto.canAccessVideoGeneration,
canAccessVoiceClone: dto.canAccessVoiceClone,
};
const cascaded = cascadePermissions(merged); 연동용 경로는 반대로 세 연동 필드만 덮고 기능 권한을 유지한다. 후속 변경은 새 사용자·신분 그룹 권한의 연동 기본값도 false로 맞췄다. 기존 행을 수정할 때 다른 운영자의 값을 보존하는 규칙과, 새 행을 만들 때 명시적 부여 전에는 연동을 열지 않는 규칙은 별개의 책임이다.
직접 쓰기를 막아도 모든 필드가 독립적이지는 않다
canAccessAiCommons가 false이면 cascade는 연동 권한을 포함한 모든 하위 권한을 false로 바꾼다. 따라서 학교 운영자가 연동 필드를 직접 보낼 수 없다는 설명은 맞지만, 학교 운영자의 변경이 연동 값을 절대 끄지 못한다는 설명은 맞지 않는다. 기능을 켠 상태의 정상 갱신에서는 연동 값을 보존하고, 상위 기능을 끄면 종속 규칙이 우선한다.
읽기·병합·쓰기는 보호하는 트랜잭션이나 버전 조건 없이 이루어진다. 두 운영자가 같은 JSON을 읽고 각각 수정하면 나중 쓰기가 먼저 쓰인 값을 오래된 스냅샷으로 덮을 수 있다. 필드별 수정 권한을 나누는 것은 동시 수정 충돌을 해결하는 것과도 다르다.
테스트 코드에는 추가 가드 메타데이터, 허용 DTO 데이터, 병합과 master-off cascade 검사가 있다. 다만 추가 필드 거부를 설명하는 일부 테스트는 새 DTO의 키 목록만 검사한다. 실제 HTTP 요청에 전역 ValidationPipe를 적용해 400을 확인한 검사는 아니다. 거부 설정과 병합 규칙의 검사를 실제 HTTP 거부나 동시 갱신 검증으로 확대해서는 안 된다.