fix(identity): 잔여 과제 전건 처리 + 로딩 표시 실렌더 확정
앞선 커밋이 CHANGELOG 에 주장한 "로딩 표시가 나오지 않던 문제 수정" 이 실제로 화면에 나타나는지 브라우저로 확정했다. 전달 경로부터 DOM 마운트·해제까지 측정했고, 지연·에러 응답에서도 영구 로딩이 남지 않음을 확인했다. 측정 과정에서 앞선 세션의 판단 하나가 틀렸음을 확인했다 — "오버레이 DOM 이 어느 화면에서도 관측되지 않는다" 는 관측 대상 노드를 잘못 잡은 것이었다. 스피너 방식 레이아웃은 다른 노드를 쓰며, 대조군에서도 안 보였던 이유가 이것이다. 미뤄 두었던 과제 4건을 함께 구현했다. - 인증창을 그냥 닫고 이탈하면 그 인증 요청을 서버에도 종료로 표시한다. 이전에는 브라우저만 알고 있어 인증 이력에 "보낸 뒤 소식 없는" 항목이 남았다 - 플러그인 설정 화면이 저장된 비밀값을 다시 내려받던 것을 가려서 표시한다. 그 항목을 건드리지 않고 저장하면 저장된 값이 그대로 유지된다 - 언어팩 반영 누락 검사가 버전만 비교해 "버전 같고 내용 다름" 을 놓치던 사각을 콘텐츠 비교로 닫았다 - 확장이 검증 규칙을 추가할 때 쓰는 필터 이름 6개를 표준 형태로 통일했다. 예전 이름도 함께 발행하므로 기존 확장은 수정 없이 동작한다 정적 검사 룰 2종의 오탐·사각을 함께 고쳤고, 개명으로 검사 대상에 편입된 파일의 사전 결함 8건을 해소했다. 검수용 데이터는 전량 정리했다.
This commit is contained in:
1 parent
bd215bc586
commit
b127afcea7
38 files changed
+503
-53
No files matched your search
@@ -2,9 +2,11 @@
|
||||
|
||||
namespace App\Http\Controllers\Api\Admin;
|
||||
|
||||
use App\Extension\PluginManager;
|
||||
use App\Http\Controllers\Api\Base\AdminBaseController;
|
||||
use App\Http\Requests\Admin\UpdatePluginSettingsRequest;
|
||||
use App\Services\PluginSettingsService;
|
||||
use App\Support\SensitiveSettingMask;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
|
||||
/**
|
||||
@@ -18,13 +20,35 @@ class PluginSettingsController extends AdminBaseController
|
||||
* PluginSettingsController 생성자
|
||||
*
|
||||
* @param PluginSettingsService $pluginSettingsService 플러그인 설정 서비스
|
||||
* @param PluginManager $pluginManager 설정 스키마(sensitive 플래그) 조회용
|
||||
*/
|
||||
public function __construct(
|
||||
private PluginSettingsService $pluginSettingsService
|
||||
private PluginSettingsService $pluginSettingsService,
|
||||
private PluginManager $pluginManager
|
||||
) {
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* 응답에 실을 설정에서 민감값을 마스크로 치환한다.
|
||||
*
|
||||
* 이 응답은 관리자 화면으로 나가므로 암호화 키·시크릿의 평문이 브라우저·개발자 도구·프록시
|
||||
* 로그에 남지 않아야 한다. 값이 저장되어 있다는 사실만 마스크로 알린다. 운영자가 값을 건드리지
|
||||
* 않으면 화면이 마스크를 그대로 되돌려 보내고, 저장 단계가 그것을 걸러 기존 값을 보존한다.
|
||||
*
|
||||
* @param string $identifier 플러그인 식별자
|
||||
* @param array<string, mixed> $settings 복호화된 설정
|
||||
* @return array<string, mixed> 마스킹된 설정
|
||||
*/
|
||||
private function maskSensitive(string $identifier, array $settings): array
|
||||
{
|
||||
$plugin = $this->pluginManager->getPlugin($identifier);
|
||||
|
||||
return $plugin === null
|
||||
? $settings
|
||||
: SensitiveSettingMask::apply($settings, $plugin->getSettingsSchema());
|
||||
}
|
||||
|
||||
/**
|
||||
* 플러그인 설정을 조회합니다.
|
||||
*
|
||||
@@ -39,7 +63,7 @@ class PluginSettingsController extends AdminBaseController
|
||||
return $this->notFound('plugins.not_found');
|
||||
}
|
||||
|
||||
return $this->success('messages.success', $settings);
|
||||
return $this->success('messages.success', $this->maskSensitive($identifier, $settings));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -65,7 +89,7 @@ class PluginSettingsController extends AdminBaseController
|
||||
|
||||
return $this->success(
|
||||
'plugins.settings.updated',
|
||||
$this->pluginSettingsService->get($identifier)
|
||||
$this->maskSensitive($identifier, $this->pluginSettingsService->get($identifier) ?? [])
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -106,19 +106,16 @@ class UpdatePluginSettingsRequest extends FormRequest
|
||||
$rules[$field] = $fieldRules;
|
||||
}
|
||||
|
||||
// audit:allow core-formrequest-hook-filter reason: 훅 이름이 표준(`{대상}.{동작}_validation_rules`)과
|
||||
// 어긋난 상태로 이미 공개되어 있다(`core.plugin_settings.update_rules`). 같은 이탈이 코어에
|
||||
// 6개 더 있으며(core.auth.validate_reset_token_rules / core.auth.verify_password_rules /
|
||||
// core.extension.changelog_rules / core.layout.get_validation_messages /
|
||||
// core.search.validation_rules / core.user.upload_avatar_rules), 이름을 바꾸면 이 훅을
|
||||
// 구독 중인 제3자 확장이 조용히 동작을 멈춘다. 일괄 개명은 PO 판단 사항이라 여기서
|
||||
// 단독으로 바꾸지 않는다.
|
||||
// 표준 이름(`core.{대상}.{동작}_validation_rules`)으로 발행하되, 이미 공개돼 구독 중일 수 있는
|
||||
// 구 이름(`core.plugin_settings.update_rules`)도 함께 발행한다 — 구 이름을 구독하는 제3자
|
||||
// 확장이 조용히 멈추지 않도록 한다. 구 이름은 구독자가 있을 때만 1회 경고가 남는다.
|
||||
//
|
||||
// 3번째 인자로 이번 요청의 입력값을 함께 넘긴다. 확장이 "현재 입력한 모드에 따라
|
||||
// 다른 필드를 필수로 만드는" 조건부 규칙을 만들려면 입력값이 필요한데, 그것이 없어
|
||||
// 확장이 request() 를 직접 들여다보던 것을 없애기 위함이다.
|
||||
// 기존 2인자 구독자는 그대로 동작한다(초과 인자는 무시된다).
|
||||
return HookManager::applyFilters(
|
||||
return HookManager::applyFiltersWithLegacyName(
|
||||
'core.plugin_settings.update_validation_rules',
|
||||
'core.plugin_settings.update_rules',
|
||||
$rules,
|
||||
$identifier,
|
||||
|
||||
@@ -13,11 +13,13 @@ class ValidateResetTokenRequest extends FormRequest
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* 공개 엔드포인트이므로 항상 통과시킨다 — 접근 제어는 미들웨어가 담당한다.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true; // 공개 엔드포인트
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -32,7 +34,8 @@ class ValidateResetTokenRequest extends FormRequest
|
||||
'email' => 'required|email',
|
||||
];
|
||||
|
||||
return HookManager::applyFilters(
|
||||
return HookManager::applyFiltersWithLegacyName(
|
||||
'core.auth.validate_reset_token_validation_rules',
|
||||
'core.auth.validate_reset_token_rules',
|
||||
$rules,
|
||||
$this
|
||||
|
||||
@@ -9,6 +9,8 @@ class VerifyPasswordRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
@@ -27,6 +29,9 @@ class VerifyPasswordRequest extends FormRequest
|
||||
];
|
||||
|
||||
// 모듈/플러그인이 validation rules를 동적으로 추가할 수 있도록 훅 제공
|
||||
return HookManager::applyFilters('core.auth.verify_password_rules', $rules);
|
||||
return HookManager::applyFiltersWithLegacyName(
|
||||
'core.auth.verify_password_validation_rules',
|
||||
'core.auth.verify_password_rules',
|
||||
$rules);
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,8 @@ class ChangelogRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
@@ -33,7 +35,10 @@ class ChangelogRequest extends FormRequest
|
||||
'to_version' => 'nullable|string|regex:/^\d+\.\d+\.\d+(-[a-zA-Z0-9]+(\.[a-zA-Z0-9]+)*)?$/|required_with:from_version',
|
||||
];
|
||||
|
||||
return HookManager::applyFilters('core.extension.changelog_rules', $rules);
|
||||
return HookManager::applyFiltersWithLegacyName(
|
||||
'core.extension.changelog_validation_rules',
|
||||
'core.extension.changelog_rules',
|
||||
$rules);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -15,6 +15,8 @@ class SearchRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
@@ -39,7 +41,10 @@ class SearchRequest extends FormRequest
|
||||
|
||||
// 모듈/플러그인이 validation rules를 동적으로 추가할 수 있도록 훅 제공
|
||||
// 예: sort의 in 규칙, board_slug 등 모듈별 파라미터
|
||||
return HookManager::applyFilters('core.search.validation_rules', $rules, $this);
|
||||
return HookManager::applyFiltersWithLegacyName(
|
||||
'core.search.index_validation_rules',
|
||||
'core.search.validation_rules',
|
||||
$rules, $this);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -24,6 +24,8 @@ class UploadAvatarRequest extends FormRequest
|
||||
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
@@ -46,7 +48,10 @@ class UploadAvatarRequest extends FormRequest
|
||||
],
|
||||
];
|
||||
|
||||
return HookManager::applyFilters('core.user.upload_avatar_rules', $rules);
|
||||
return HookManager::applyFiltersWithLegacyName(
|
||||
'core.user.upload_avatar_validation_rules',
|
||||
'core.user.upload_avatar_rules',
|
||||
$rules);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in new issue
Block a user